Separate entity from rigidbody

This commit is contained in:
John Alanbrook 2024-05-02 17:13:09 -05:00
parent e86e126894
commit 79e4772f93
9 changed files with 460 additions and 473 deletions

View file

@ -177,7 +177,7 @@ Object.mixin(os.sprite(true), {
height() { return this.dimensions().y; }, height() { return this.dimensions().y; },
}); });
globalThis.allsprites = {}; globalThis.allsprites = {};
os.sprite(true).make = function(go) os.sprite().make = function(go)
{ {
var sp = os.sprite(); var sp = os.sprite();
sp.go = go; sp.go = go;

View file

@ -344,7 +344,7 @@ var editor = {
root = root ? root + "." : root; root = root ? root + "." : root;
Object.entries(obj.objects).forEach(function(x) { Object.entries(obj.objects).forEach(function(x) {
var p = root + x[0]; var p = root + x[0];
render.text(p, x[1].screenpos(), 1, editor.color_depths[depth]); render.text(p, x[1].this2screen(), 1, editor.color_depths[depth]);
editor.draw_objects_names(x[1], p, depth+1); editor.draw_objects_names(x[1], p, depth+1);
}); });
}, },
@ -402,7 +402,7 @@ var editor = {
if (this.comp_info && this.sel_comp) if (this.comp_info && this.sel_comp)
render.text(input.print_pawn_kbm(this.sel_comp,false), [100,700],1); render.text(input.print_pawn_kbm(this.sel_comp,false), [100,700],1);
render.cross(editor.edit_level.screenpos(),3,Color.blue); render.cross(editor.edit_level.this2screen(),3,Color.blue);
var thiso = editor.get_this(); var thiso = editor.get_this();
var clvl = thiso; var clvl = thiso;
@ -447,16 +447,16 @@ var editor = {
render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]); render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
this.selectlist.forEach(function(x) { this.selectlist.forEach(function(x) {
render.text(x.urstr(), x.screenpos().add([0, render.font.linegap*2]), 1, Color.editor.ur); render.text(x.urstr(), x.this2screen().add([0, render.font.linegap*2]), 1, Color.editor.ur);
render.text(x.pos.map(function(x) { return Math.round(x); }), x.screenpos()); render.text(x.pos.map(function(x) { return Math.round(x); }), x.this2screen());
render.cross(x.screenpos(), 10, Color.blue); render.cross(x.this2screen(), 10, Color.blue);
}); });
Object.entries(thiso.objects).forEach(function(x) { Object.entries(thiso.objects).forEach(function(x) {
var p = x[1].namestr(); var p = x[1].namestr();
render.text(p, x[1].screenpos().add([0,render.font.linegap]),1,editor.color_depths[depth]); render.text(p, x[1].this2screen().add([0,render.font.linegap]),1,editor.color_depths[depth]);
render.point(x[1].screenpos(),5,Color.blue.alpha(0.3)); render.point(x[1].this2screen(),5,Color.blue.alpha(0.3));
render.point(x[1].screenpos(), 1, Color.red); render.point(x[1].this2screen(), 1, Color.red);
}); });
var mg = physics.pos_query(input.mouse.worldpos()); var mg = physics.pos_query(input.mouse.worldpos());
@ -474,7 +474,7 @@ var editor = {
for (var key in this.selectlist[0].components) { for (var key in this.selectlist[0].components) {
var selected = this.sel_comp === this.selectlist[0].components[key]; var selected = this.sel_comp === this.selectlist[0].components[key];
var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]"; var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]";
render.text(str, this.selectlist[0].screenpos().add([0,-render.font.linegap*(i++)])); render.text(str, this.selectlist[0].this2screen().add([0,-render.font.linegap*(i++)]));
} }
if (this.sel_comp) { if (this.sel_comp) {
@ -879,7 +879,7 @@ editor.inputs['C-s'] = function() {
} }
var savejs = saveobj.json_obj(); var savejs = saveobj.json_obj();
var tur = saveobj.get_ur(); var tur = saveobj.ur;
if (!tur) { if (!tur) {
console.warn(`Can't save object because it has no ur.`); console.warn(`Can't save object because it has no ur.`);
return; return;

View file

@ -289,7 +289,6 @@ game.engine_start = function(s) {
gggstart(function() { gggstart(function() {
global.mixin("scripts/sound.js"); global.mixin("scripts/sound.js");
world_start(); world_start();
go_init();
window.set_icon(os.make_texture("icons/moon.gif")) window.set_icon(os.make_texture("icons/moon.gif"))
Object.readonly(window.__proto__, 'vsync'); Object.readonly(window.__proto__, 'vsync');
Object.readonly(window.__proto__, 'enable_dragndrop'); Object.readonly(window.__proto__, 'enable_dragndrop');
@ -330,9 +329,9 @@ function process()
} }
var st = profile.now(); var st = profile.now();
if (!game.camera) if (!game.camera)
prosperon.window_render(world, 1); prosperon.window_render(world.transform, 1);
else else
prosperon.window_render(game.camera, game.camera.zoom); prosperon.window_render(game.camera.transform, game.camera.zoom);
render.set_camera(); render.set_camera();
@ -627,7 +626,8 @@ global.mixin("scripts/actor");
global.mixin("scripts/entity"); global.mixin("scripts/entity");
function world_start() { function world_start() {
globalThis.world = os.make_gameobject(); globalThis.world = Object.create(entity);
world.transform = os.make_transform2d();
world.objects = {}; world.objects = {};
world.toString = function() { return "world"; }; world.toString = function() { return "world"; };
world.ur = "world"; world.ur = "world";

View file

@ -12,16 +12,398 @@ function obj_unique_name(name, obj) {
return n; return n;
} }
var gameobject = { function unique_name(list, name = "new_object") {
var str = name.replaceAll('.', '_');
var n = 1;
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
}
return t;
};
var entity = {
get_comp_by_name(name) { get_comp_by_name(name) {
var comps = []; var comps = [];
for (var c of Object.values(this.components)) for (var c of Object.values(this.components))
if (c.comp === name) comps.push(c); if (c.comp === name) comps.push(c);
if (comps.length) return comps; if (comps.length) return comps;
return undefined; return undefined;
}, },
path_from(o) {
var p = this.toString();
var c = this.master;
while (c && c !== o && c !== world) {
p = c.toString() + "." + p;
c = c.master;
}
if (c === world) p = "world." + p;
return p;
},
full_path() { return this.path_from(world); },
clear() {
for (var k in this.objects) {
this.objects[k].kill();
};
this.objects = {};
},
sync() {
this.components.forEach(function(x) { x.sync?.(); });
this.objects.forEach(function(x) { x.sync(); });
},
delay(fn, seconds) {
var timers = this.timers;
var stop = function() {
timers.remove(stop);
execute = undefined;
stop = undefined;
rm?.();
rm = undefined;
update = undefined;
}
function execute() {
fn();
stop?.();
}
stop.remain = seconds;
stop.seconds = seconds;
stop.pct = function() { return 1 - (stop.remain/stop.seconds); };
function update(dt) {
stop.remain -= dt;
if (stop.remain <= 0) execute();
}
var rm = Register.update.register(update);
timers.push(stop);
return stop;
},
cry(file) { return audio.cry(file); },
get pos() { return this.transform.pos; },
set pos(x) { this.transform.pos = x; },
get angle() { return this.transform.angle; },
set angle(x) { this.transform.angle = x; },
get scale() { return this.transform.scale; },
set scale(x) { this.transform.scale = x; },
move(vec) { this.pos = this.pos.add(vec); },
rotate(x) { this.angle += x; },
grow(vec) {
if (typeof vec === 'number') vec = [vec,vec];
this.scale = this.scale.map((x,i) => x*vec[i]);
},
/* Reparent 'this' to be 'parent's child */
reparent(parent) {
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
console.spam(`parenting ${this.toString()} to ${parent.toString()}`);
if (this.master === parent) {
console.warn("not reparenting ...");
console.warn(`${this.master} is the same as ${parent}`);
return;
}
var name = unique_name(Object.keys(parent), this.name);
this.name = name;
this.master?.remove_obj(this);
this.master = parent;
parent.objects[this.guid] = this;
parent[name] = this;
Object.hide(parent, name);
},
remove_obj(obj) {
delete this.objects[obj.guid];
delete this[obj.name];
Object.unhide(this, obj.name);
},
spawn(text, config, callback) {
var st = profile.now();
var ent = Object.create(entity);
ent.transform = os.make_transform2d();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
if (!text)
ent.ur = emptyur;
else if (typeof text === 'object' && text) {// assume it's an ur
ent.ur = text;
text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat();
}
else {
ent.ur = getur(text, config);
text = ent.ur.text;
config = [ent.ur.data, config];
}
if (typeof text === 'string')
use(text, ent);
else if (Array.isArray(text))
text.forEach(path => use(path,ent));
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config))
config.forEach(function(path) {
if (typeof path === 'string') {
console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path)));
}
else if (typeof path === 'object')
Object.merge(ent,path);
});
ent.reparent(this);
for (var [prop, p] of Object.entries(ent)) {
if (!p) continue;
if (typeof p !== 'object') continue;
if (component.isComponent(p)) continue;
if (!p.comp) continue;
ent[prop] = component[p.comp].make(ent);
Object.merge(ent[prop], p);
ent.components[prop] = ent[prop];
};
check_registers(ent);
if (typeof ent.load === 'function') ent.load();
if (sim.playing())
if (typeof ent.start === 'function') ent.start();
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master');
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {}
};
Object.hide(ent, '_ed');
ent.sync();
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
ent.objects = {};
for (var i in o) {
console.info(`creating ${i} on ${ent.toString()}`);
var newur = o[i].ur;
delete o[i].ur;
var n = ent.spawn(ur[newur], o[i]);
ent.rename_obj(n.toString(), i);
}
}
if (ent.tag) game.tag_add(ent.tag, ent);
if (callback) callback(ent);
ent.ur.fresh ??= json.decode(json.encode(ent));
ent.ur.fresh.objects = {};
for (var i in ent.objects)
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
profile.addreport(entityreport, ent.ur.name, st);
return ent;
},
disable() { this.components.forEach(function(x) { x.disable(); }); },
enable() { this.components.forEach(function(x) { x.enable(); }); },
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
/* Make a unique object the same as its prototype */
revert() { Object.merge(this, this.ur.fresh); },
name: "new_object",
toString() { return this.name; },
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t - bb.b;
},
flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; },
mirror(plane) { this.scale = Vector.reflect(this.scale, plane); },
/* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({
t: 0,
r: 0,
b: 0,
l: 0
});
for (var key in this.components) {
if ('boundingbox' in this.components[key])
boxes.push(this.components[key].boundingbox());
}
for (var key in this.objects)
boxes.push(this.objects[key].boundingbox());
var bb = boxes.shift();
boxes.forEach(function(x) { bb = bbox.expand(bb, x); });
bb = bbox.move(bb, this.pos);
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
/* The unique components of this object. Its diff. */
json_obj(depth=0) {
var fresh = this.ur.fresh;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
var d = ediff(thiso, fresh);
d ??= {};
fresh.objects ??= {};
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, fresh.objects);
if (odiff)
d.objects = curobjs;
delete d.pos;
delete d.angle;
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
/* The object needed to store an object as an instance of a master */
instance_obj() {
var t = this.transform();
t.ur = this.ur.name;
return t;
},
transform() {
var t = {};
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
t.angle = Math.places(this.get_angle(this.master), 4);
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));;
return t;
},
dup(diff) {
var n = this.master.spawn(this.ur);
Object.totalmerge(n, this.transform());
return n;
},
kill() {
if (this.__kill) return;
this.__kill = true;
console.spam(`Killing entity of type ${this.ur}`);
this.timers.forEach(t => t());
this.timers = [];
Event.rm_obj(this);
input.do_uncontrol(this);
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
}
for (var key in this.components) {
this.components[key].kill?.();
this.components[key].gameobject = undefined;
this[key].enabled = false;
delete this.components[key];
delete this[key];
}
delete this.components;
this.clear();
if (typeof this.stop === 'function') this.stop();
game.tag_clear_guid(this.guid);
for (var i in this) {
if (typeof this[i] === 'object') delete this[i];
if (typeof this[i] === 'function') delete this[i];
}
},
make_objs(objs) {
for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`);
var newobj = this.spawn(objs[prop]);
}
},
rename_obj(name, newname) {
if (!this.objects[name]) {
console.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];
},
add_component(comp, data, name = comp.toString()) {
if (typeof comp.make !== 'function') return;
name = obj_unique_name(name, this);
this[name] = comp.make(this);
this[name].comp = comp.toString();
this.components[name] = this[name];
if (data)
Object.assign(this[name], data);
return this[name];
},
};
var gameobject = {
check_dirty() { check_dirty() {
this._ed.urdiff = this.json_obj(); this._ed.urdiff = this.json_obj();
this._ed.dirty = !Object.empty(this._ed.urdiff); this._ed.dirty = !Object.empty(this._ed.urdiff);
@ -50,9 +432,6 @@ var gameobject = {
return str; return str;
}, },
full_path() {
return this.path_from(world);
},
/* pin this object to the to object */ /* pin this object to the to object */
pin(to) { pin(to) {
var p = joint.pin(this,to); var p = joint.pin(this,to);
@ -95,63 +474,6 @@ var gameobject = {
var p = joint.motor(this, to, rate); var p = joint.motor(this, to, rate);
}, },
path_from(o) {
var p = this.toString();
var c = this.master;
while (c && c !== o && c !== world) {
p = c.toString() + "." + p;
c = c.master;
}
if (c === world) p = "world." + p;
return p;
},
clear() {
for (var k in this.objects) {
this.objects[k].kill();
};
this.objects = {};
},
delay(fn, seconds) {
var timers = this.timers;
var stop = function() {
timers.remove(stop);
execute = undefined;
stop = undefined;
rm?.();
rm = undefined;
update = undefined;
}
function execute() {
fn();
stop?.();
}
stop.remain = seconds;
stop.seconds = seconds;
stop.pct = function() { return 1 - (stop.remain/stop.seconds); };
function update(dt) {
stop.remain -= dt;
if (stop.remain <= 0) execute();
}
var rm = Register.update.register(update);
timers.push(stop);
return stop;
},
cry(file) { return audio.cry(file); },
set pos(x) { this.set_pos(x); },
get pos() { return this.rpos; },
set angle(x) { this.set_angle(x); },
get angle() { return this.rangle; },
set scale(x) { this.set_scale(x); },
get scale() { return this.rscale; },
set_pos(x, relative = world) { set_pos(x, relative = world) {
var newpos = relative.this2world(x); var newpos = relative.this2world(x);
var move = newpos.sub(this.pos); var move = newpos.sub(this.pos);
@ -197,285 +519,8 @@ var gameobject = {
return this.scale.map((x,i) => x/masterscale[i]); return this.scale.map((x,i) => x/masterscale[i]);
}, },
/* Moving, rotating, scaling functions, world relative */
move(vec) { this.set_pos(this.pos.add(vec)); },
rotate(x) { this.set_angle(this.angle + x); },
grow(vec) {
if (typeof vec === 'number') vec = [vec,vec,vec];
this.set_scale(this.scale.map((x, i) => x * vec[i]));
},
screenpos() { return game.camera.world2view(this.pos); },
get_ur() { return this.ur; },
/* spawn an entity
text can be:
the file path of a script
an ur object
nothing
*/
spawn(text, config, callback) {
var st = profile.now();
var ent = os.make_gameobject();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
Object.mixin(ent, {
set category(n) {
if (n === 0) {
this.categories = n;
return;
}
var cat = (1 << (n-1));
this.categories = cat;
},
get category() {
if (this.categories === 0) return 0;
var pos = 0;
var num = this.categories;
while (num > 0) {
if (num & 1) {
break;
}
pos++;
num >>>= 1;
}
return pos+1;
}
});
if (typeof text === 'object' && text) {// assume it's an ur
ent.ur = text;
text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat();
}
else {
ent.ur = getur(text, config);
text = ent.ur.text;
config = [ent.ur.data, config];
}
if (typeof text === 'string')
use(text, ent);
else if (Array.isArray(text))
text.forEach(path => use(path,ent));
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config))
config.forEach(function(path) {
if (typeof path === 'string') {
console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path)));
}
else if (typeof path === 'object')
Object.merge(ent,path);
});
ent.reparent(this);
for (var [prop, p] of Object.entries(ent)) {
if (!p) continue;
if (typeof p !== 'object') continue;
if (component.isComponent(p)) continue;
if (!p.comp) continue;
ent[prop] = component[p.comp].make(ent);
Object.merge(ent[prop], p);
ent.components[prop] = ent[prop];
};
check_registers(ent);
if (typeof ent.load === 'function') ent.load();
if (sim.playing())
if (typeof ent.start === 'function') ent.start();
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master', 'categories');
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {}
};
Object.hide(ent, '_ed');
ent.sync();
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
ent.objects = {};
for (var i in o) {
console.info(`creating ${i} on ${ent.toString()}`);
var newur = o[i].ur;
delete o[i].ur;
var n = ent.spawn(ur[newur], o[i]);
ent.rename_obj(n.toString(), i);
}
}
if (ent.tag) game.tag_add(ent.tag, ent);
if (callback) callback(ent);
ent.ur.fresh ??= json.decode(json.encode(ent));
ent.ur.fresh.objects = {};
for (var i in ent.objects)
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
profile.addreport(entityreport, ent.ur.name, st);
return ent;
},
/* Reparent 'this' to be 'parent's child */
reparent(parent) {
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
console.spam(`parenting ${this.toString()} to ${parent.toString()}`);
if (this.master === parent) {
console.warn("not reparenting ...");
console.warn(`${this.master} is the same as ${parent}`);
return;
}
this.master?.remove_obj(this);
this.master = parent;
function unique_name(list, name = "new_object") {
var str = name.replaceAll('.', '_');
var n = 1;
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
}
return t;
};
var name = unique_name(Object.keys(parent.objects), this.ur.name);
parent.objects[name] = this;
parent[name] = this;
Object.hide(parent, name);
this.toString = function() { return name; };
},
remove_obj(obj) {
delete this.objects[obj.toString()];
delete this[obj.toString()];
Object.unhide(this, obj.toString());
},
components: {},
objects: {},
master: undefined,
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
in_air() { return this.in_air(); }, in_air() { return this.in_air(); },
hide() { this.components.forEach(x => x.hide?.());
this.objects.forEach(x => x.hide?.()); },
show() { this.components.forEach(function(x) { x.show?.(); });
this.objects.forEach(function(x) { x.show?.(); }); },
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t - bb.b;
},
/* Make a unique object the same as its prototype */
revert() { Object.merge(this, this.ur.fresh); },
toString() { return "new_object"; },
flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; },
mirror(plane) { this.scale = Vector.reflect(this.scale, plane); },
disable() { this.components.forEach(function(x) { x.disable(); }); },
enable() { this.components.forEach(function(x) { x.enable(); }); },
/* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({
t: 0,
r: 0,
b: 0,
l: 0
});
for (var key in this.components) {
if ('boundingbox' in this.components[key])
boxes.push(this.components[key].boundingbox());
}
for (var key in this.objects)
boxes.push(this.objects[key].boundingbox());
var bb = boxes.shift();
boxes.forEach(function(x) { bb = bbox.expand(bb, x); });
bb = bbox.move(bb, this.pos);
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
/* The unique components of this object. Its diff. */
json_obj(depth=0) {
var fresh = this.ur.fresh;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
var d = ediff(thiso, fresh);
d ??= {};
fresh.objects ??= {};
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, fresh.objects);
if (odiff)
d.objects = curobjs;
delete d.pos;
delete d.angle;
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
/* The object needed to store an object as an instance of a master */
instance_obj() {
var t = this.transform();
t.ur = this.ur.name;
return t;
},
transform() {
var t = {};
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
t.angle = Math.places(this.get_angle(this.master), 4);
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));;
return t;
},
/* Velocity and angular velocity of the object */ /* Velocity and angular velocity of the object */
phys_obj() { phys_obj() {
var phys = {}; var phys = {};
@ -483,111 +528,32 @@ var gameobject = {
phys.angularvelocity = this.angularvelocity; phys.angularvelocity = this.angularvelocity;
return phys; return phys;
}, },
phys_mat() { set category(n) {
return { if (n === 0) {
friction: this.friction, this.categories = n;
elasticity: this.elasticity return;
} }
var cat = (1 << (n-1));
this.categories = cat;
}, },
get category() {
dup(diff) { if (this.categories === 0) return 0;
var n = this.master.spawn(this.ur); var pos = 0;
Object.totalmerge(n, this.transform()); var num = this.categories;
return n; while (num > 0) {
}, if (num & 1) {
break;
kill() { }
if (this.__kill) return; pos++;
this.__kill = true; num >>>= 1;
console.spam(`Killing entity of type ${this.ur}`);
this.timers.forEach(t => t());
this.timers = [];
Event.rm_obj(this);
input.do_uncontrol(this);
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
} }
for (var key in this.components) {
this.components[key].kill?.();
this.components[key].gameobject = undefined;
this[key].enabled = false;
delete this.components[key];
delete this[key];
}
delete this.components;
this.clear();
if (typeof this.stop === 'function') this.stop();
game.tag_clear_guid(this.guid); return pos+1;
for (var i in this) {
if (typeof this[i] === 'object') delete this[i];
if (typeof this[i] === 'function') delete this[i];
}
},
up() { return [0, 1].rotate(this.angle); },
down() { return [0, -1].rotate(this.angle); },
right() { return [1, 0].rotate(this.angle); },
left() { return [-1, 0].rotate(this.angle); },
make_objs(objs) {
for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`);
var newobj = this.spawn(objs[prop]);
}
},
rename_obj(name, newname) {
if (!this.objects[name]) {
console.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];
},
add_component(comp, data, name = comp.toString()) {
if (typeof comp.make !== 'function') return;
name = obj_unique_name(name, this);
this[name] = comp.make(this);
this[name].comp = comp.toString();
this.components[name] = this[name];
if (data)
Object.assign(this[name], data);
return this[name];
},
}
function go_init() {
var gop = os.make_gameobject().__proto__;
Object.mixin(gop, gameobject);
gop.sync = function() {
this.selfsync();
this.components.forEach(function(x) { x.sync?.(); });
this.objects.forEach(function(x) { x.sync?.(); });
} }
} }
gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`; entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
gameobject.doc = { gameobject.doc = {
doc: "All objects in the game created through spawning have these attributes.", doc: "All objects in the game created through spawning have these attributes.",
@ -695,8 +661,18 @@ function apply_ur(u, ent) {
} }
} }
var emptyur = {
name: "empty"
}
var getur = function(text, data) var getur = function(text, data)
{ {
if (!text && !data) {
console.info('empty ur');
return {
name: "empty"
};
}
var urstr = text + "+" + data; var urstr = text + "+" + data;
if (!ur[urstr]) { if (!ur[urstr]) {
ur[urstr] = { ur[urstr] = {
@ -784,4 +760,4 @@ game.ur.save = function(str)
} }
} }
return { go_init } return { entity }

View file

@ -84,8 +84,6 @@ void model_free(model *m);
void model_draw_go(model *m, gameobject *go, gameobject *cam); void model_draw_go(model *m, gameobject *go, gameobject *cam);
void model_init();
material *material_make(); material *material_make();
void material_free(material *mat); void material_free(material *mat);

View file

@ -204,9 +204,3 @@ HMM_Vec3 go_pos3d(gameobject *go)
HMM_Vec2 pos2d = go_pos(go); HMM_Vec2 pos2d = go_pos(go);
return (HMM_Vec3){pos2d.x, pos2d.y, go->drawlayer}; return (HMM_Vec3){pos2d.x, pos2d.y, go->drawlayer};
} }
void gameobject_draw_debug(gameobject *go) {
if (!go || !go->body) return;
cpBodyEachShape(go->body, body_draw_shapes_dbg, NULL);
}

View file

@ -779,7 +779,21 @@ JSC_CCALL(render_setuniproj,
) )
JSC_CCALL(render_setunim4, JSC_CCALL(render_setunim4,
HMM_Mat4 m = transform2d2mat4(js2transform2d(argv[2])); HMM_Mat4 m = MAT1;
if (JS_IsArray(js, argv[2])) {
JSValue arr = argv[2];
int n = js_arrlen(arr);
if (n == 1)
m = transform2d2mat4(js2transform2d(js_getpropidx(arr,0)));
else {
for (int i = 0; i < n; i++) {
HMM_Mat4 p = transform2d2mat4(js2transform2d(js_getpropidx(arr, i)));
m = HMM_MulM4(p,m);
}
}
} else
m = transform2d2mat4(js2transform2d(argv[2]));
sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(m.e)); sg_apply_uniforms(js2number(argv[0]), js2number(argv[1]), SG_RANGE_REF(m.e));
); );
@ -920,7 +934,7 @@ 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, js2transform2d(argv[0]), js2number(argv[1])))
JSC_CCALL(prosperon_guid, JSC_CCALL(prosperon_guid,
uint8_t bytes[16]; uint8_t bytes[16];
for (int i = 0; i < 16; i++) bytes[i] = rand()%256; for (int i = 0; i < 16; i++) bytes[i] = rand()%256;
@ -1089,8 +1103,6 @@ static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, mod,1) MIST_FUNC_DEF(io, mod,1)
}; };
JSC_CCALL(debug_draw_gameobject, gameobject_draw_debug(js2gameobject(argv[0]));)
JSC_GETSET_GLOBAL(disabled_color, color) JSC_GETSET_GLOBAL(disabled_color, color)
JSC_GETSET_GLOBAL(sleep_color, color) JSC_GETSET_GLOBAL(sleep_color, color)
JSC_GETSET_GLOBAL(dynamic_color, color) JSC_GETSET_GLOBAL(dynamic_color, color)
@ -1098,7 +1110,6 @@ JSC_GETSET_GLOBAL(kinematic_color, color)
JSC_GETSET_GLOBAL(static_color, color) JSC_GETSET_GLOBAL(static_color, color)
static const JSCFunctionListEntry js_debug_funcs[] = { static const JSCFunctionListEntry js_debug_funcs[] = {
MIST_FUNC_DEF(debug, draw_gameobject, 1),
CGETSET_ADD(global, disabled_color), CGETSET_ADD(global, disabled_color),
CGETSET_ADD(global, sleep_color), CGETSET_ADD(global, sleep_color),
CGETSET_ADD(global, dynamic_color), CGETSET_ADD(global, dynamic_color),
@ -1716,7 +1727,16 @@ JSC_SCALL(os_make_texture,
JSC_CCALL(os_make_font, return font2js(MakeFont(js2str(argv[0]), js2number(argv[1])))) JSC_CCALL(os_make_font, return font2js(MakeFont(js2str(argv[0]), js2number(argv[1]))))
JSC_CCALL(os_make_transform2d, JSC_CCALL(os_make_transform2d,
return transform2d2js(make_transform2d()); if (JS_IsUndefined(argv[0]))
return transform2d2js(make_transform2d());
int n = js2number(argv[0]);
transform2d *t = calloc(sizeof(transform2d), n);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
js_setprop_num(arr, i, transform2d2js(t+i));
return arr;
) )
JSC_SCALL(os_system, return number2js(system(str)); ) JSC_SCALL(os_system, return number2js(system(str)); )
@ -1742,7 +1762,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_texture, 1), MIST_FUNC_DEF(os, make_texture, 1),
MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_model, 1), MIST_FUNC_DEF(os, make_model, 1),
MIST_FUNC_DEF(os, make_transform2d, 0), MIST_FUNC_DEF(os, make_transform2d, 1),
}; };
#include "steam.h" #include "steam.h"

View file

@ -258,8 +258,6 @@ void render_init() {
font_init(); font_init();
debugdraw_init(); debugdraw_init();
model_init();
sg_color c = (sg_color){0,0,0,1}; sg_color c = (sg_color){0,0,0,1};
pass_action = (sg_pass_action){ pass_action = (sg_pass_action){
.colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = c}, .colors[0] = {.load_action = SG_LOADACTION_CLEAR, .clear_value = c},
@ -337,7 +335,7 @@ HMM_Mat4 useproj = {0};
#define MODE_EXPAND 4 #define MODE_EXPAND 4
#define MODE_FULL 5 #define MODE_FULL 5
void openglRender(struct window *window, gameobject *cam, float zoom) { void openglRender(struct window *window, transform2d *cam, float zoom) {
sg_swapchain sch = sglue_swapchain(); sg_swapchain sch = sglue_swapchain();
sg_begin_pass(&(sg_pass){ sg_begin_pass(&(sg_pass){
.action = pass_action, .action = pass_action,
@ -372,7 +370,7 @@ void openglRender(struct window *window, gameobject *cam, float zoom) {
} }
// 2D projection // 2D projection
campos = go_pos(cam); campos = cam->pos;
camzoom = zoom; camzoom = zoom;
projection = HMM_Orthographic_RH_NO( projection = HMM_Orthographic_RH_NO(

View file

@ -14,6 +14,7 @@
#include "sokol/sokol_gfx.h" #include "sokol/sokol_gfx.h"
#include "HandmadeMath.h" #include "HandmadeMath.h"
#include "gameobject.h" #include "gameobject.h"
#include "transform.h"
#define RGBA_MAX 255 #define RGBA_MAX 255
@ -61,7 +62,7 @@ void render_init();
extern HMM_Vec2 campos; extern HMM_Vec2 campos;
extern float camzoom; extern float camzoom;
void openglRender(struct window *window, gameobject *cam, float zoom); void openglRender(struct window *window, transform2d *cam, float zoom);
void opengl_rendermode(enum RenderMode r); void opengl_rendermode(enum RenderMode r);
void openglInit3d(struct window *window); void openglInit3d(struct window *window);