prosperon/scripts/entity.js

770 lines
20 KiB
JavaScript
Raw Normal View History

2024-04-14 14:53:41 -05:00
globalThis.entityreport = {};
function obj_unique_name(name, obj) {
2024-09-26 11:36:09 -05:00
name = name.replaceAll(".", "_");
2023-12-24 09:14:46 -06:00
if (!(name in obj)) return name;
var t = 1;
var n = name + t;
while (n in obj) {
t++;
n = name + t;
2023-12-24 09:14:46 -06:00
}
return n;
}
2024-05-02 17:13:09 -05:00
function unique_name(list, name = "new_object") {
2024-09-26 11:36:09 -05:00
var str = name.replaceAll(".", "_");
2024-05-02 17:13:09 -05:00
var n = 1;
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
}
return t;
2024-09-26 11:36:09 -05:00
}
2024-05-02 17:13:09 -05:00
var entity = {
2024-07-22 19:07:02 -05:00
drawlayer: -1,
2023-12-20 09:19:04 -06:00
get_comp_by_name(name) {
var comps = [];
2024-09-26 11:36:09 -05:00
for (var c of Object.values(this.components)) if (c.comp === name) comps.push(c);
2023-12-20 09:19:04 -06:00
if (comps.length) return comps;
return undefined;
},
2024-09-26 11:36:09 -05:00
2024-05-28 13:39:02 -05:00
rigidify() {
this.body = os.make_body(this.transform);
},
2024-09-26 11:36:09 -05:00
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;
},
2024-09-26 11:36:09 -05:00
drawlayer: 0,
2024-09-26 11:36:09 -05:00
full_path() {
return this.path_from(world);
},
clear() {
2024-09-30 19:00:09 -05:00
for (var k in this.objects)
this.objects[k].kill();
this.objects = {};
},
2024-05-02 17:13:09 -05:00
sync() {
2024-07-25 16:14:37 -05:00
for (var c of Object.values(this.components)) c.sync?.();
for (var o of Object.values(this.objects)) o.sync();
2024-05-02 17:13:09 -05:00
},
2024-09-30 10:14:56 -05:00
delay(fn, seconds) { prosperon.add_timer(this, fn, seconds); },
2024-09-26 11:36:09 -05:00
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.transform.rotate(x, [0, 0, -1]);
},
2024-05-02 17:13:09 -05:00
grow(vec) {
2024-09-26 11:36:09 -05:00
if (typeof vec === "number") vec = [vec, vec];
this.scale = this.scale.map((x, i) => x * vec[i]);
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
/* Reparent 'this' to be 'parent's child */
reparent(parent) {
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
if (this.master === parent) {
2024-08-28 13:49:24 -05:00
console.warn(`not reparenting ... ${this.master} is the same as ${parent}`);
2024-05-02 17:13:09 -05:00
return;
}
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
var name = unique_name(Object.keys(parent), this.name);
this.name = name;
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
this.master?.remove_obj(this);
this.master = parent;
parent.objects[this.guid] = this;
parent[name] = this;
Object.hide(parent, name);
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
remove_obj(obj) {
2024-09-26 11:36:09 -05:00
if (this.objects) delete this.objects[obj.guid];
else console.warn(`Object ${this.guid} has no objects file.`);
2024-05-02 17:13:09 -05:00
delete this[obj.name];
Object.unhide(this, obj.name);
2024-04-09 08:01:54 -05:00
},
2024-09-26 11:36:09 -05:00
2024-04-01 17:58:29 -05:00
spawn(text, config, callback) {
2024-10-02 09:55:32 -05:00
profile.report(`make_${text}`);
2024-09-26 11:36:09 -05:00
var ent = class_use(text, config, entity, function (ent) {
2024-08-05 15:26:18 -05:00
ent.transform = os.make_transform();
ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
2024-08-05 15:26:18 -05:00
ent.ur = {};
2024-08-22 13:31:00 -05:00
ent.urname = text;
2024-08-05 15:26:18 -05:00
});
2024-09-26 11:36:09 -05:00
/*
2024-05-02 17:13:09 -05:00
if (!text)
ent.ur = emptyur;
2024-07-25 16:14:37 -05:00
else if (text instanceof Object) {// assume it's an ur
2024-04-04 17:28:11 -05:00
ent.ur = text;
2024-04-12 13:53:00 -05:00
text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat();
}
else {
2024-04-04 17:28:11 -05:00
ent.ur = getur(text, config);
2024-04-12 13:53:00 -05:00
text = ent.ur.text;
config = [ent.ur.data, config];
}
2024-08-05 15:26:18 -05:00
2024-04-04 17:28:11 -05:00
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
2024-04-04 17:28:11 -05:00
else if (Array.isArray(config))
2024-07-25 16:14:37 -05:00
for (var path of config) {
2024-04-11 17:17:49 -05:00
if (typeof path === 'string') {
console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path)));
2024-04-11 17:17:49 -05:00
}
2024-07-25 16:14:37 -05:00
else if (path instanceof Object)
Object.merge(ent,path);
2024-07-25 16:14:37 -05:00
};
2024-08-05 15:26:18 -05:00
if (typeof text === 'string') {
class_use(
use(text, ent);
}
else if (Array.isArray(text))
for (var path of text) use(path,ent);
*/
2024-04-03 17:17:32 -05:00
ent.reparent(this);
2024-09-26 11:36:09 -05:00
for (var [prop, p] of Object.entries(ent)) {
if (!p) continue;
2024-09-26 11:36:09 -05:00
if (typeof p !== "object") continue;
if (!p.comp) continue;
2024-05-28 13:39:02 -05:00
ent[prop] = component[p.comp](ent);
Object.merge(ent[prop], p);
ent.components[prop] = ent[prop];
2024-09-26 11:36:09 -05:00
}
2024-07-25 16:14:37 -05:00
check_registers(ent);
2024-07-25 16:14:37 -05:00
2024-09-13 17:56:02 -05:00
if (ent.awake instanceof Function) ent.awake();
2024-08-29 16:55:01 -05:00
if (sim.playing()) {
ent._started = true;
2024-07-25 16:14:37 -05:00
if (ent.start instanceof Function) ent.start();
2024-08-29 16:55:01 -05:00
}
2024-09-26 11:36:09 -05:00
Object.hide(ent, "ur", "components", "objects", "timers", "guid", "master", "guid");
2024-04-03 17:17:32 -05:00
ent._ed = {
selectable: true,
dirty: false,
inst: false,
2024-09-26 11:36:09 -05:00
urdiff: {},
2024-04-03 17:17:32 -05:00
};
2024-09-26 11:36:09 -05:00
Object.hide(ent, "_ed");
ent.sync();
2024-09-26 11:36:09 -05:00
if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
2024-04-11 17:17:49 -05:00
ent.objects = {};
for (var i in o) {
2024-09-30 04:36:53 -05:00
console.info(`creating ${i} on ${ent.toString()}`);
2024-04-04 17:28:11 -05:00
var newur = o[i].ur;
delete o[i].ur;
2024-04-04 17:28:11 -05:00
var n = ent.spawn(ur[newur], o[i]);
ent.rename_obj(n.toString(), i);
}
}
2024-09-26 11:36:09 -05:00
2024-04-03 00:44:08 -05:00
if (ent.tag) game.tag_add(ent.tag, ent);
2024-09-26 11:36:09 -05:00
2024-04-03 08:37:29 -05:00
if (callback) callback(ent);
2024-09-26 11:36:09 -05:00
2024-04-04 17:28:11 -05:00
ent.ur.fresh ??= json.decode(json.encode(ent));
2024-04-09 08:01:54 -05:00
ent.ur.fresh.objects = {};
2024-09-26 11:36:09 -05:00
for (var i in ent.objects) ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
2024-05-21 18:50:53 -05:00
2024-10-02 09:55:32 -05:00
profile.endreport(`make_${text}`);
2024-09-26 11:36:09 -05:00
return ent;
},
2024-09-26 11:36:09 -05:00
disable() {
for (var x of this.components) x.disable();
},
enable() {
for (var x of this.components) x.enable();
},
this2screen(pos) {
return game.camera.world2view(this.this2world(pos));
},
screen2this(pos) {
return this.world2this(game.camera.view2world(pos));
},
2024-05-02 17:13:09 -05:00
/* Make a unique object the same as its prototype */
2024-09-26 11:36:09 -05:00
revert() {
Object.merge(this, this.ur.fresh);
},
2024-05-02 17:13:09 -05:00
name: "new_object",
2024-09-26 11:36:09 -05:00
toString() {
return this.name;
},
2023-10-04 17:57:37 -05:00
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
2024-09-26 11:36:09 -05:00
2023-10-04 17:57:37 -05:00
height() {
var bb = this.boundingbox();
return bb.t - bb.b;
2023-10-04 17:57:37 -05:00
},
2024-09-26 11:36:09 -05:00
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,
2024-09-26 11:36:09 -05:00
l: 0,
});
2024-09-26 11:36:09 -05:00
for (var key in this.components) {
2024-09-26 11:36:09 -05:00
if ("boundingbox" in this.components[key]) boxes.push(this.components[key].boundingbox());
}
2024-09-26 11:36:09 -05:00
for (var key in this.objects) boxes.push(this.objects[key].boundingbox());
var bb = boxes.shift();
2024-09-26 11:36:09 -05:00
2024-07-25 16:14:37 -05:00
for (var x of boxes) bb = bbox.expand(bb, x);
2024-09-26 11:36:09 -05:00
bb = bbox.move(bb, this.pos);
2024-09-26 11:36:09 -05:00
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
2024-08-08 13:25:47 -05:00
toJSON() {
2024-09-26 11:36:09 -05:00
return { guid: this.guid };
2024-08-08 13:25:47 -05:00
},
2024-09-26 11:36:09 -05:00
/* The unique components of this object. Its diff. */
2024-09-26 11:36:09 -05:00
json_obj(depth = 0) {
2024-04-04 17:28:11 -05:00
var fresh = this.ur.fresh;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
2024-04-03 17:17:32 -05:00
var d = ediff(thiso, fresh);
2024-09-26 11:36:09 -05:00
d ??= {};
2024-09-26 11:36:09 -05:00
2024-04-03 17:17:32 -05:00
fresh.objects ??= {};
var curobjs = {};
2024-09-26 11:36:09 -05:00
for (var o in this.objects) curobjs[o] = this.objects[o].instance_obj();
2024-04-03 17:17:32 -05:00
var odiff = ediff(curobjs, fresh.objects);
2024-09-26 11:36:09 -05:00
if (odiff) d.objects = curobjs;
delete d.pos;
delete d.angle;
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
2024-09-26 11:36:09 -05:00
/* The object needed to store an object as an instance of a master */
instance_obj() {
2024-09-01 13:59:34 -05:00
var t = os.make_transform();
t = this.transform;
2024-04-04 17:28:11 -05:00
t.ur = this.ur.name;
return t;
},
2024-09-26 11:36:09 -05:00
transform() {
var t = {};
2024-04-09 16:48:15 -05:00
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
2024-04-09 08:01:54 -05:00
t.angle = Math.places(this.get_angle(this.master), 4);
2024-09-26 11:36:09 -05:00
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));
return t;
},
2024-09-26 11:36:09 -05:00
dup(diff) {
2024-04-04 17:28:11 -05:00
var n = this.master.spawn(this.ur);
2024-04-09 16:48:15 -05:00
Object.totalmerge(n, this.transform());
return n;
},
2024-09-26 11:36:09 -05:00
2023-11-29 12:40:13 -06:00
kill() {
2023-12-28 12:49:48 -06:00
if (this.__kill) return;
this.__kill = true;
this.timers.forEachRight(x => x());
delete this.timers;
2024-01-03 17:19:13 -06:00
Event.rm_obj(this);
2024-04-03 08:37:29 -05:00
input.do_uncontrol(this);
2024-09-26 11:36:09 -05:00
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
2023-11-29 12:40:13 -06:00
}
2024-09-26 11:36:09 -05:00
2023-11-29 12:40:13 -06:00
for (var key in this.components) {
this.components[key].kill?.();
2023-12-19 15:34:36 -06:00
this.components[key].gameobject = undefined;
2024-05-28 13:39:02 -05:00
this.components[key].enabled = false;
2023-12-18 17:12:05 -06:00
delete this.components[key];
2023-11-29 12:40:13 -06:00
}
2024-09-26 11:36:09 -05:00
delete this.components;
2024-09-26 11:36:09 -05:00
2023-11-29 12:40:13 -06:00
this.clear();
2024-07-25 16:14:37 -05:00
if (this.stop instanceof Function) this.stop();
2024-09-26 11:36:09 -05:00
if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === "function") this.then();
2024-04-03 00:44:08 -05:00
game.tag_clear_guid(this.guid);
2024-09-25 12:46:59 -05:00
rmactor(this);
2024-09-26 11:36:09 -05:00
for (var i in this) {
2024-07-25 16:14:37 -05:00
if (this[i] instanceof Object || this[i] instanceof Function) delete this[i];
}
2023-11-29 12:40:13 -06:00
},
2024-09-26 11:36:09 -05:00
2023-10-09 18:10:10 -05:00
make_objs(objs) {
for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`);
var newobj = this.spawn(objs[prop]);
2023-10-09 18:10:10 -05:00
}
},
2024-09-26 11:36:09 -05:00
2023-10-02 17:03:01 -05:00
rename_obj(name, newname) {
if (!this.objects[name]) {
2024-09-30 04:36:53 -05:00
console.warn(`No object with name ${name}. Could not rename to ${newname}.`);
2023-10-02 17:03:01 -05:00
return;
}
2023-10-26 11:48:02 -05:00
if (name === newname) {
Object.hide(this, name);
return;
}
2024-09-26 11:36:09 -05:00
if (this.objects[newname]) return;
2023-10-02 17:03:01 -05:00
this.objects[newname] = this.objects[name];
this[newname] = this[name];
2024-09-26 11:36:09 -05:00
this[newname].toString = function () {
return newname;
};
2023-10-26 11:48:02 -05:00
Object.hide(this, newname);
2023-10-02 17:03:01 -05:00
delete this.objects[name];
delete this[name];
2023-10-02 17:03:01 -05:00
return this.objects[newname];
},
2024-09-26 11:36:09 -05:00
2024-05-28 13:39:02 -05:00
add_component(comp, data) {
var name = prosperon.guid();
this.components[name] = comp(this);
if (data) {
Object.assign(this.components[name], data);
this.components[name].sync?.();
}
return this.components[name];
},
2024-05-02 17:13:09 -05:00
};
2024-05-02 17:13:09 -05:00
var gameobject = {
check_dirty() {
this._ed.urdiff = this.json_obj();
this._ed.dirty = !Object.empty(this._ed.urdiff);
return; // TODO: IMPLEMENT
var lur = this.master.ur;
if (!lur) return;
var lur = lur.objects[this.toString()];
var d = ediff(this._ed.urdiff, lur);
2024-09-26 11:36:09 -05:00
if (!d || Object.empty(d)) this._ed.inst = true;
else this._ed.inst = false;
2024-05-02 17:13:09 -05:00
},
namestr() {
var s = this.toString();
if (this._ed?.dirty)
if (this._ed.inst) s += "#";
else s += "*";
return s;
},
urstr() {
var str = this.ur.name;
if (this._ed.dirty) str = "*" + str;
return str;
},
/* pin this object to the to object */
pin(to) {
2024-09-26 11:36:09 -05:00
var p = joint.pin(this, to);
2024-05-02 17:13:09 -05:00
},
2024-09-26 11:36:09 -05:00
slide(to, a = [0, 0], b = [0, 0], min = 0, max = 50) {
2024-05-02 17:13:09 -05:00
var p = joint.slide(this, to, a, b, min, max);
p.max_force = 500;
p.break();
},
pivot(to, piv = this.pos) {
var p = joint.pivot(this, to, piv);
},
/* groove is on to, from local points a and b, anchored to this at local anchor */
2024-09-26 11:36:09 -05:00
groove(to, a, b, anchor = [0, 0]) {
2024-05-02 17:13:09 -05:00
var p = joint.groove(to, this, a, b, anchor);
},
2024-09-26 11:36:09 -05:00
damped_spring(to, length = Vector.length(this.pos, to.pos), stiffness = 1, damping = 1) {
2024-05-02 17:13:09 -05:00
var dc = 2 * Math.sqrt(stiffness * this.mass);
var p = joint.damped_spring(this, to, [0, 0], [0, 0], stiffness, damping * dc);
},
damped_rotary_spring(to, angle = 0, stiffness = 1, damping = 1) {
/* calculate actual damping value from the damping ratio */
/* damping = 1 is critical */
var dc = 2 * Math.sqrt(stiffness * this.get_moi()); /* critical damping number */
/* zeta = actual/critical */
var p = joint.damped_rotary(this, to, angle, stiffness, damping * dc);
},
rotary_limit(to, min, max) {
var p = joint.rotary(this, to, Math.turn2rad(min), Math.turn2rad(max));
},
ratchet(to, ratch) {
var phase = this.angle - to.angle;
var p = joint.ratchet(this, to, phase, Math.turn2rad(ratch));
},
gear(to, ratio = 1, phase = 0) {
var phase = this.angle - to.angle;
var p = joint.gear(this, to, phase, ratio);
},
motor(to, rate) {
var p = joint.motor(this, to, rate);
},
set_pos(x, relative = world) {
var newpos = relative.this2world(x);
var move = newpos.sub(this.pos);
this.rpos = newpos;
2024-07-25 16:14:37 -05:00
for (var o of this.objects) o.move(move);
2024-05-02 17:13:09 -05:00
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
set_angle(x, relative = world) {
var newangle = relative.angle + x;
var diff = newangle - this.angle;
this.rangle = newangle;
2024-07-25 16:14:37 -05:00
for (var obj of this.objects) {
2024-05-02 17:13:09 -05:00
obj.rotate(diff);
obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master);
2024-07-25 16:14:37 -05:00
}
2024-05-02 17:13:09 -05:00
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
set_scale(x, relative = world) {
2024-09-26 11:36:09 -05:00
if (typeof x === "number") x = [x, x, x];
var newscale = relative.scale.map((s, i) => x[i] * s);
var pct = this.scale.map((s, i) => newscale[i] / s);
2024-05-02 17:13:09 -05:00
this.rscale = newscale;
2024-07-25 16:14:37 -05:00
for (var obj of this.objects) {
2024-05-02 17:13:09 -05:00
obj.grow(pct);
2024-09-26 11:36:09 -05:00
obj.set_pos(
obj.get_pos(obj.master).map((x, i) => x * pct[i]),
obj.master,
);
}
2024-05-02 17:13:09 -05:00
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
get_pos(relative = world) {
if (relative === world) return this.pos;
return relative.world2this(this.pos);
//return this.pos.sub(relative.pos);
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
get_angle(relative = world) {
if (relative === world) return this.angle;
return this.angle - relative.angle;
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
get_scale(relative = world) {
if (relative === world) return this.scale;
var masterscale = relative.scale;
2024-09-26 11:36:09 -05:00
return this.scale.map((x, i) => x / masterscale[i]);
},
in_air() {
return this.in_air();
2024-05-02 17:13:09 -05:00
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
/* Velocity and angular velocity of the object */
phys_obj() {
var phys = {};
phys.velocity = this.velocity;
phys.angularvelocity = this.angularvelocity;
return phys;
},
2024-09-26 11:36:09 -05:00
2024-05-02 17:13:09 -05:00
set category(n) {
if (n === 0) {
this.categories = n;
return;
}
2024-09-26 11:36:09 -05:00
var cat = 1 << (n - 1);
2024-05-02 17:13:09 -05:00
this.categories = cat;
},
get category() {
if (this.categories === 0) return 0;
var pos = 0;
var num = this.categories;
while (num > 0) {
2024-09-26 11:36:09 -05:00
if (num & 1) {
break;
}
pos++;
num >>>= 1;
2024-05-02 17:13:09 -05:00
}
2024-09-26 11:36:09 -05:00
return pos + 1;
},
};
2024-05-02 17:13:09 -05:00
entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
2023-09-20 13:33:11 -05:00
gameobject.doc = {
doc: "All objects in the game created through spawning have these attributes.",
pos: "Position of the object, relative to its master.",
angle: "Rotation of this object, relative to its master.",
velocity: "Velocity of the object, relative to world.",
angularvelocity: "Angular velocity of the object, relative to the world.",
scale: "Scale of the object, relative to its master.",
2023-11-15 16:42:39 -06:00
flipx: "Check if the object is flipped on its x axis.",
flipy: "Check if the object is flipped on its y axis.",
2024-03-19 23:01:31 -05:00
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find √their resultant velocities.`,
friction: `When one object touches another, friction slows them down.`,
mass: `The higher the mass of the object, the less forces will affect it.`,
2024-03-09 18:22:06 -06:00
phys: `Set to 0, 1, or 2, representing dynamic, kinematic, and static.`,
worldpos: `Function returns the world position of the object.`,
set_pos: `Function to set the position of the object in world coordinates.`,
worldangle: `Function to get the angle of the entity in the world.`,
rotate: `Function to rotate this object by x degrees.`,
2024-09-26 11:36:09 -05:00
move: "Move an object by x,y,z. If the first parameter is an array, uses up to the first three array values.",
pulse: `Apply an impulse to this body in world coordinates. Impulse is a short force.`,
shove: `Apply a force to this body in world coordinates. Should be used over many frames.`,
2024-09-26 11:36:09 -05:00
shove_at: "Apply a force to this body, at a position relative to itself.",
max_velocity: "The max linear velocity this object can travel.",
max_angularvelocity: "The max angular velocity this object can rotate.",
on_ground: `Return true if the object is on the ground.`,
spawn: `Create an instance of a supplied ur-type on this object. Optionally provide a data object to modify the created entity.`,
hide: `Make this object invisible.`,
show: `Make this object visible.`,
width: `The total width of the object and all its components.`,
height: `The total height of the object.`,
move: `Move this object the given amount.`,
boundingbox: `The boundingbox of the object.`,
dup: `Make an exact copy of this object.`,
transform: `Return an object representing the transform state of this object.`,
kill: `Remove this object from the world.`,
master: "The entity this entity belongs to.",
2024-09-26 11:36:09 -05:00
delay: "Run the given function after the given number of seconds has elapsed.",
cry: "Make a sound. Can only make one at a time.",
add_component: "Add a component to the object by name.",
pin: "Pin joint to another object. Acts as if a rigid rod is between the two objects.",
slide: "Slide joint, similar to a pin but with min and max allowed distances.",
pivot: "Pivot joint to an object, with the pivot given in world coordinates.",
groove: "Groove joint. The groove is on to, from to local coordinates a and b, with this object anchored at anchor.",
damped_spring: "Damped spring to another object. Length is the distance it wants to be, stiffness is the spring constant, and damping is the damping ratio. 1 is critical, < 1 is underdamped, > 1 is overdamped.",
damped_rotary_spring: "Similar to damped spring but for rotation. Rest angle is the attempted angle.",
rotary_limit: "Limit the angle relative to the to body between min and max.",
ratchet: "Like a socket wrench, relative to to. ratch is the distance between clicks.",
gear: "Keeps the angular velocity ratio of this body and to constant. Ratio is the gear ratio.",
motor: "Keeps the relative angular velocity of this body to to at a constant rate. The most simple idea is for one of the bodies to be static, to the other is kept at rate.",
layer: "Bitmask for collision layers.",
drawlayer: "Layer for drawing. Higher numbers draw above lower ones.",
warp_filter: "Bitmask for selecting what warps should affect this entity.",
};
global.ur = {};
2024-09-26 11:36:09 -05:00
if (io.exists(`${io.dumpfolder}/ur.json`)) ur = json.decode(io.slurp(`${io.dumpfolder}/ur.json`));
else {
ur = {};
ur._list = [];
}
/* UR OBJECT
ur {
name: fully qualified name of ur
text: file path to the script
data: file path to data
proto: resultant object of a freshly made entity
}
*/
/* Apply an ur u to an entity e */
/* u is given as */
2024-04-11 07:38:28 -05:00
function apply_ur(u, ent) {
2024-09-26 11:36:09 -05:00
if (typeof u !== "string") {
console.warn("Must give u as a string.");
return;
}
2024-09-26 11:36:09 -05:00
var urs = u.split(".");
2024-04-11 07:38:28 -05:00
if (!urs.every(u => ur[u])) {
console.error(`Attempted to make ur combo ${u} but not every ur in the chain exists.`);
return;
}
2024-09-26 11:36:09 -05:00
2024-04-11 07:38:28 -05:00
for (var u of urs) {
var text = u.text;
var data = u.data;
2024-09-26 11:36:09 -05:00
if (typeof text === "string") use(text, ent);
else if (Array.isArray(text)) for (var path of text) use(path, ent);
if (typeof data === "string") Object.merge(ent, json.decode(Resources.replstrs(data)));
2024-04-11 07:38:28 -05:00
else if (Array.isArray(data)) {
2024-07-25 16:14:37 -05:00
for (var path of data) {
2024-09-26 11:36:09 -05:00
if (typeof path === "string") Object.merge(ent, json.decode(Resources.replstrs(data)));
else if (path instanceof Object) Object.merge(ent, path);
}
2024-04-11 07:38:28 -05:00
}
}
}
2024-05-02 17:13:09 -05:00
var emptyur = {
2024-09-26 11:36:09 -05:00
name: "empty",
};
2024-05-02 17:13:09 -05:00
2024-09-26 11:36:09 -05:00
var getur = function (text, data) {
2024-05-02 17:13:09 -05:00
if (!text && !data) {
2024-09-26 11:36:09 -05:00
console.info("empty ur");
2024-05-02 17:13:09 -05:00
return {
2024-09-26 11:36:09 -05:00
name: "empty",
2024-05-02 17:13:09 -05:00
};
}
2024-07-24 14:17:32 -05:00
var urstr = text;
2024-09-26 11:36:09 -05:00
if (data) urstr += "+" + data;
2024-07-24 14:17:32 -05:00
2024-04-04 17:28:11 -05:00
if (!ur[urstr]) {
ur[urstr] = {
name: urstr,
text: text,
2024-09-26 11:36:09 -05:00
data: data,
};
2024-04-04 17:28:11 -05:00
}
return ur[urstr];
2024-09-26 11:36:09 -05:00
};
2024-04-04 17:28:11 -05:00
2024-09-26 11:36:09 -05:00
var ur_from_file = function (file) {
2024-04-11 07:38:28 -05:00
var urname = file.name();
if (ur[urname]) {
console.warn(`Tried to make another ur with the name ${urname} from ${file}, but it already exists.`);
return undefined;
}
var newur = {
2024-09-26 11:36:09 -05:00
name: urname,
2024-04-11 07:38:28 -05:00
};
ur[urname] = newur;
ur._list.push(urname);
return newur;
2024-09-26 11:36:09 -05:00
};
2024-04-11 07:38:28 -05:00
2024-09-26 11:36:09 -05:00
game.loadurs = function () {
2024-09-25 20:51:20 -05:00
return;
ur = {};
ur._list = [];
/* FIND ALL URS IN A PROJECT */
2024-04-04 17:28:11 -05:00
for (var file of io.glob("**.ur")) {
2024-04-11 07:38:28 -05:00
var newur = ur_from_file(file);
if (!newur) continue;
2024-04-12 13:53:00 -05:00
var uur = Resources.replstrs(file);
var urjson = json.decode(uur);
2024-04-11 07:38:28 -05:00
Object.assign(newur, urjson);
2024-04-04 17:28:11 -05:00
}
2024-09-26 11:36:09 -05:00
2024-04-14 14:53:41 -05:00
for (var file of io.glob("**.jso").filter(f => !ur[f.name()])) {
2024-09-26 11:36:09 -05:00
if (file[0] === "." || file[0] === "_") continue;
2024-04-11 07:38:28 -05:00
var newur = ur_from_file(file);
if (!newur) continue;
2024-09-26 11:36:09 -05:00
newur.text = file;
2024-04-11 17:17:49 -05:00
var data = file.set_ext(".json");
if (io.exists(data)) {
console.info(`Found matching json ${data} for ${file}`);
newur.data = data;
}
}
2024-03-01 11:45:06 -06:00
};
2024-04-12 13:53:00 -05:00
game.ur = {};
2024-09-26 11:36:09 -05:00
game.ur.load = function (str) {};
game.ur.add_data = function (str, data) {
2024-04-12 13:53:00 -05:00
var nur = ur[str];
if (!nur) {
console.warn(`Cannot add data to the ur ${str}.`);
return;
}
if (!Array.isArray(ur.data)) {
var arr = [];
if (ur.data) arr.push(ur.data);
ur.data = arr;
}
2024-09-26 11:36:09 -05:00
2024-04-12 13:53:00 -05:00
ur.data.push(data);
2024-09-26 11:36:09 -05:00
};
2024-04-12 13:53:00 -05:00
2024-09-26 11:36:09 -05:00
game.ur.save = function (str) {
2024-04-12 13:53:00 -05:00
var nur = ur[str];
if (!nur) {
console.warn(`Cannot save ur ${str}.`);
return;
}
2024-09-26 11:36:09 -05:00
};
2024-04-12 13:53:00 -05:00
2024-09-26 11:36:09 -05:00
return { entity };