prosperon/scripts/entity.js

934 lines
24 KiB
JavaScript
Raw Normal View History

2023-11-07 12:45:52 -06:00
var actor = {};
2023-11-29 12:40:13 -06:00
actor.spawn = function(script, config){
if (typeof script !== 'string') return;
var padawan = Object.create(actor);
compile_env(script, padawan, "script");
2023-11-07 12:45:52 -06:00
2023-11-29 12:40:13 -06:00
if (typeof config === 'object')
Object.merge(padawan, config);
2023-11-29 17:31:41 -06:00
padawan.padawans = [];
padawan.timers = [];
padawan.master = this;
Object.hide(padawan, "master","timers");
this.padawans.push(padawan);
2023-11-29 12:40:13 -06:00
return padawan;
};
actor.die = function(actor){
};
2023-11-29 17:31:41 -06:00
actor.timers = [];
actor.kill = function(){
this.timers.forEach(t => t.kill());
this.master.remove(this);
this.padawans.forEach(p => p.kill());
this.__dead__ = true;
2023-11-29 12:40:13 -06:00
};
2023-11-29 17:31:41 -06:00
actor.toJSON = function() {
if (this.__dead__) return undefined;
return this;
}
2023-11-29 12:40:13 -06:00
actor.delay = function(fn, seconds) {
2023-11-29 17:31:41 -06:00
var t = Object.create(timer);
t.remain = seconds;
t.kill = () => {
timer.kill.call(t);
this.timers.remove(t);
}
t.fire = () => {
if (this.__dead__) return;
fn();
t.kill();
};
Register.appupdate.register(t.update, t);
2023-11-29 12:40:13 -06:00
this.timers.push(t);
return function() { t.kill(); };
};
actor.clock = function(fn){};
2023-11-29 17:31:41 -06:00
actor.master = undefined;
actor.padawans = [];
actor.remaster = function(to){
this.master.padawans.remove(this);
this.master = to;
to.padawans.push(this);
};
2023-11-27 14:29:55 -06:00
2023-09-08 12:35:06 -05:00
var gameobject = {
2023-11-29 12:40:13 -06:00
full_path() {
return this.path_from(Primum);
},
path_from(o) {
var p = this.toString();
var c = this.level;
while (c && c !== o && c !== Primum) {
p = c.toString() + "." + p;
c = c.level;
}
if (c === Primum) p = "Primum." + p;
return p;
},
clear() {
for (var k in this.objects) {
Log.info(`Killing ${k}`);
this.objects[k].kill();
};
this.objects = {};
},
2023-11-07 12:45:52 -06:00
delay(fn, seconds) {
var t = timer.delay(fn.bind(this), seconds, false);
2023-11-29 12:40:13 -06:00
var killfn = function() { t.kill(); };
this.timers.push(killfn);
return killfn;
},
tween(prop, values, def){
var t = Tween.make(this, prop, values, def);
t.play();
var k = function() { t.pause(); }
this.timers.push(k);
return k;
},
cry(file) {
2023-11-27 14:29:55 -06:00
var p = Sound.play(file, Sound.bus.sfx);
var killfn = p.kill.bind(p);
2023-11-27 17:04:04 -06:00
p.end = killfn;
2023-11-27 14:29:55 -06:00
this.timers.push(killfn);
return killfn;
2023-11-07 12:45:52 -06:00
},
set max_velocity(x) { cmd(151, this.body, x); },
get max_velocity() { return cmd(152, this.body); },
set max_angularvelocity(x) { cmd(154, this.body, Math.deg2rad(x)); },
get max_angularvelocity() { return Math.rad2deg(cmd(155, this.body)); },
set torque(x) { if (!(x >= 0 && x <= Infinity)) return; cmd(153, this.body, x); },
gscale() { return cmd(103,this.body); },
2023-11-14 09:20:09 -06:00
sgscale(x) {
if (typeof x === 'number')
2023-11-15 16:42:39 -06:00
x = [x,x];
cmd(36,this.body,x)
2023-11-14 09:20:09 -06:00
},
get scale() {
if (!this.level) return this.gscale();
2023-11-15 16:42:39 -06:00
return this.gscale().map((x,i) => x/this.level.gscale()[i]);
},
set scale(x) {
2023-11-15 16:42:39 -06:00
if (typeof x === 'number')
x = [x,x];
if (this.level) {
var g = this.level.gscale();
x = x.map((y,i) => y * g[i]);
}
2023-11-14 09:20:09 -06:00
2023-11-15 16:42:39 -06:00
var pct = x.map(function(y,i) { return y/this.gscale()[i]; }, this);
2023-11-15 16:42:39 -06:00
this.sgscale(x);
/* TRANSLATE ALL SUB OBJECTS */
},
2023-10-04 17:57:37 -05:00
set pos(x) {
2023-11-29 12:40:13 -06:00
if (!this.level)
this.set_worldpos(x);
else
this.set_worldpos(this.level.this2world(x));
2023-10-04 17:57:37 -05:00
},
2023-12-11 08:36:45 -06:00
get pos() {
2023-10-26 11:48:02 -05:00
if (!this.level) return this.worldpos();
2023-11-15 16:42:39 -06:00
return this.level.world2this(this.worldpos());
},
get draw_layer() { return cmd(171, this.body); },
set draw_layer(x) { cmd(172, this.body, x); },
2023-11-15 16:42:39 -06:00
2023-10-04 17:57:37 -05:00
get elasticity() { return cmd(107,this.body); },
set elasticity(x) { cmd(106,this.body,x); },
2023-10-04 17:57:37 -05:00
get friction() { return cmd(109,this.body); },
set friction(x) { cmd(108,this.body,x); },
set mass(x) { set_body(7,this.body,x); },
get mass() {
if (!(this.phys === Physics.dynamic))
return this.__proto__.mass;
return q_body(5, this.body);
},
set gravity(x) { cmd(158,this.body, x); },
get gravity() { return cmd(159,this.body); },
2023-11-20 07:49:14 -06:00
set_gravity(x) { cmd(167, this.body, x); },
set timescale(x) { cmd(168,this.body,x); },
get timescale() { return cmd(169,this.body); },
2023-11-29 17:31:41 -06:00
set phys(x) { console.warn(`Setting phys to ${x}`); set_body(1, this.body, x); },
2023-10-04 17:57:37 -05:00
get phys() { return q_body(0,this.body); },
get velocity() { return q_body(3, this.body); },
set velocity(x) { set_body(9, this.body, x); },
2023-11-29 12:40:13 -06:00
get damping() { return cmd(157,this.body); },
2023-11-20 07:49:14 -06:00
set_damping(x) { cmd(156, this.body, x); },
2023-10-04 17:57:37 -05:00
get angularvelocity() { return Math.rad2deg(q_body(4, this.body)); },
set angularvelocity(x) { set_body(8, this.body, Math.deg2rad(x)); },
2023-10-02 17:03:01 -05:00
worldpos() { return q_body(1,this.body); },
set_worldpos(x) {
2023-11-15 16:42:39 -06:00
var poses = this.objects.map(x => x.pos);
set_body(2,this.body,x);
2023-11-15 16:42:39 -06:00
this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i])));
},
2023-10-05 17:30:17 -05:00
worldangle() { return Math.rad2deg(q_body(2,this.body))%360; },
2023-10-26 11:48:02 -05:00
sworldangle(x) { set_body(0,this.body,Math.deg2rad(x)); },
2023-10-05 17:30:17 -05:00
get angle() {
if (!this.level) return this.worldangle();
return this.worldangle() - this.level.worldangle();
},
set angle(x) {
var diff = x - this.angle;
var thatpos = this.pos;
this.objects.forEach(function(x) {
2023-10-05 17:30:17 -05:00
x.rotate(diff);
var opos = x.pos;
var r = Vector.length(opos);
var p = Math.rad2deg(Math.atan2(opos.y, opos.x));
p += diff;
p = Math.deg2rad(p);
2023-10-05 17:30:17 -05:00
x.pos = [r*Math.cos(p), r*Math.sin(p)];
2023-10-26 11:48:02 -05:00
});
2023-11-29 12:40:13 -06:00
if (this.level)
set_body(0,this.body, Math.deg2rad(x - this.level.worldangle()));
else
set_body(0,this.body,x);
2023-10-05 17:30:17 -05:00
},
rotate(x) {
2023-10-26 11:48:02 -05:00
this.sworldangle(this.worldangle()+x);
},
2023-10-02 17:03:01 -05:00
2023-10-10 17:37:58 -05:00
spawn_from_instance(inst) {
2023-10-11 17:22:41 -05:00
return this.spawn(inst.ur, inst);
2023-10-10 17:37:58 -05:00
},
2023-10-11 17:22:41 -05:00
2023-10-10 17:37:58 -05:00
spawn(ur, data) {
if (typeof ur === 'string')
ur = prototypes.get_ur(ur);
if (!ur) {
Log.error("Failed to make UR from " + ur);
return undefined;
}
2023-10-10 17:37:58 -05:00
var go = ur.make(this, data);
2023-10-26 11:48:02 -05:00
Object.hide(this, go.toString());
return go;
},
2023-10-10 17:37:58 -05:00
/* Reparent 'this' to be 'parent's child */
2023-10-04 17:57:37 -05:00
reparent(parent) {
if (this.level === parent)
2023-10-10 17:37:58 -05:00
return;
this.level?.remove_obj(this);
2023-10-04 17:57:37 -05:00
this.level = parent;
2023-12-11 08:36:45 -06:00
cmd(208,parent.body,this.body);
2023-10-10 17:37:58 -05:00
function unique_name(list, obj) {
var str = obj.toString().replaceAll('.', '_');
var n = 1;
var t = str;
while (t in list) {
2023-10-10 17:37:58 -05:00
t = str + n;
n++;
}
return t;
};
var name = unique_name(parent, this.ur);
2023-10-10 17:37:58 -05:00
parent.objects[name] = this;
parent[name] = this;
2023-10-10 17:37:58 -05:00
this.toString = function() { return name; };
2023-10-04 17:57:37 -05:00
},
2023-10-10 17:37:58 -05:00
2023-10-04 17:57:37 -05:00
remove_obj(obj) {
2023-10-10 17:37:58 -05:00
if (this[obj.toString()] === this.objects[obj.toString()])
delete this[obj.toString()];
delete this.objects[obj.toString()];
delete this[obj.toString()];
2023-10-04 17:57:37 -05:00
},
2023-11-30 10:47:59 -06:00
2023-10-10 17:37:58 -05:00
components: {},
objects: {},
2023-10-04 17:57:37 -05:00
level: undefined,
get_moi() { return q_body(6, this.body); },
set_moi(x) {
if(x <= 0) {
Log.error("Cannot set moment of inertia to 0 or less.");
return;
}
set_body(13, this.body, x);
},
2023-10-04 17:57:37 -05:00
2023-10-26 11:48:02 -05:00
pulse(vec) { set_body(4, this.body, vec);},
shove(vec) { set_body(12,this.body,vec);},
shove_at(vec, at) { set_body(14,this.body,vec,at); },
2023-10-26 11:48:02 -05:00
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body,pos); },
dir_world2this(dir) { return cmd(160, this.body, dir); },
dir_this2world(dir) { return cmd(161, this.body, dir); },
2023-10-26 11:48:02 -05:00
set layer(x) { cmd(75,this.body,x); },
2023-11-07 17:24:26 -06:00
get layer() { cmd(77,this.body); },
2023-10-26 11:48:02 -05:00
alive() { return this.body >= 0; },
in_air() { return q_body(7, this.body);},
2023-10-04 17:57:37 -05:00
hide() { this.components.forEach(function(x) { x.hide(); }); this.objects.forEach(function(x) { x.hide(); }); },
show() { this.components.forEach(function(x) { x.show(); }); this.objects.forEach(function(x) { x.show(); }); },
get_relangle() {
if (!this.level) return this.angle;
return this.angle - this.level.angle;
},
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t-bb.b;
},
2023-12-11 08:36:45 -06:00
move(vec) { this.pos = this.pos.add(vec); },
2023-10-09 13:03:12 -05:00
2023-12-11 08:36:45 -06:00
rotate(amt) { this.angle += amt; },
2023-10-09 13:03:12 -05:00
2023-10-04 17:57:37 -05:00
/* Make a unique object the same as its prototype */
revert() {
2023-11-29 12:40:13 -06:00
// var keys = Object.samenewkeys(this, this.__proto__);
// keys.unique.forEach(x => delete this[x]);
// keys.same.forEach(x => this[x] = this.__proto__[x]);
var jobj = this.json_obj();
var lobj = this.level.__proto__.objects[this.toString()];
delete jobj.objects;
Object.keys(jobj).forEach(function(x) {
if (lobj && x in lobj)
this[x] = lobj[x];
else
this[x] = this.__proto__[x];
}, this);
2023-10-10 17:37:58 -05:00
this.sync();
2023-10-04 17:57:37 -05:00
},
check_registers(obj) {
Register.unregister_obj(obj);
if (typeof obj.update === 'function')
Register.update.register(obj.update, obj);
if (typeof obj.physupdate === 'function')
Register.physupdate.register(obj.physupdate, obj);
if (typeof obj.collide === 'function')
obj.register_hit(obj.collide, obj);
if (typeof obj.separate === 'function')
obj.register_separate(obj.separate, obj);
if (typeof obj.draw === 'function')
Register.draw.register(obj.draw,obj);
if (typeof obj.debug === 'function')
Register.debug.register(obj.debug, obj);
2023-11-07 12:45:52 -06:00
if (typeof obj.gui === 'function')
Register.gui.register(obj.gui,obj);
2023-11-22 03:51:43 -06:00
for (var k in obj) {
if (!k.startswith("on_")) continue;
var signal = k.fromfirst("on_");
Event.observe(signal, obj, obj[k]);
};
2023-10-04 17:57:37 -05:00
obj.components.forEach(function(x) {
if (typeof x.collide === 'function')
register_collide(1, x.collide, x, obj.body, x.shape);
});
},
2023-11-29 12:40:13 -06:00
2023-11-15 16:42:39 -06:00
flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; },
mirror(plane) {
this.scale = Vector.reflect(this.scale, plane);
},
2023-10-04 17:57:37 -05:00
save:true,
selectable:true,
ed_locked:false,
2023-11-29 12:40:13 -06:00
disable() { this.components.forEach(function(x) { x.disable(); });},
enable() { this.components.forEach(function(x) { x.enable(); });},
sync() {
this.components.forEach(function(x) { x.sync(); });
this.objects.forEach(function(x) { x.sync(); });
},
2023-10-04 17:57:37 -05:00
/* 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());
}
2023-09-29 13:16:59 -05:00
for (var key in this.objects)
boxes.push(this.objects[key].boundingbox());
if (boxes.empty) return cwh2bb([0,0], [0,0]);
var bb = boxes[0];
boxes.forEach(function(x) {
bb = bb_expand(bb, x);
});
var cwh = bb2cwh(bb);
if (!bb) return;
if (this.flipx) cwh.c.x *= -1;
if (this.flipy) cwh.c.y *= -1;
cwh.c = cwh.c.add(this.pos);
bb = cwh2bb(cwh.c, cwh.wh);
return bb ? bb : cwh2bb([0,0], [0,0]);
},
2023-10-02 17:03:01 -05:00
json_obj() {
2023-10-05 17:30:17 -05:00
var d = ediff(this,this.__proto__);
2023-10-09 18:10:10 -05:00
d ??= {};
var objects = {};
this.__proto__.objects ??= {};
2023-10-10 17:37:58 -05:00
var curobjs = {};
for (var o in this.objects)
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, this.__proto__.objects);
if (odiff)
d.objects = curobjs;
2023-10-09 18:10:10 -05:00
delete d.pos;
delete d.angle;
delete d.velocity;
delete d.angularvelocity;
return d;
},
2023-10-26 11:48:02 -05:00
transform_obj() {
var t = this.json_obj();
Object.assign(t, this.transform());
return t;
},
2023-09-29 13:16:59 -05:00
2023-10-10 17:37:58 -05:00
instance_obj() {
var t = this.transform_obj();
t.ur = this.ur;
return t;
},
ur_obj() {
var ur = this.json_obj();
for (var k in ur)
if (ur[k].ur)
delete ur[k];
return ur;
},
2023-09-26 08:37:19 -05:00
make_ur() {
var thisur = this.json_obj();
thisur.pos = this.pos;
thisur.angle = this.angle;
2023-09-26 08:37:19 -05:00
return thisur;
},
2023-09-26 13:34:02 -05:00
transform() {
var t = {};
t.pos = this.pos.map(Number.prec);
t.angle = Number.prec(this.angle);
2023-09-26 13:34:02 -05:00
return t;
},
phys_obj() {
var phys = {};
phys.velocity = this.velocity.map(Number.prec);
phys.angularvelocity = Number.prec(this.angularvelocity);
return phys;
},
2023-09-26 08:37:19 -05:00
dup(diff) {
2023-10-04 17:57:37 -05:00
var n = this.level.spawn(this.__proto__);
2023-09-26 08:37:19 -05:00
Object.totalmerge(n, this.make_ur());
return n;
},
2023-10-11 17:22:41 -05:00
2023-11-29 12:40:13 -06:00
kill() {
this.timers.forEach(t => t());
if (this.level) {
this.level.remove_obj(this);
this.level = undefined;
}
2023-11-29 12:40:13 -06:00
Player.do_uncontrol(this);
Register.unregister_obj(this);
2023-11-29 12:40:13 -06:00
if (this.__proto__.instances)
this.__proto__.instances.remove(this);
2023-11-29 12:40:13 -06:00
for (var key in this.components) {
Register.unregister_obj(this.components[key]);
Log.info(`Destroying component ${key}`);
this.components[key].kill();
this.components.gameobject = undefined;
}
2023-11-29 12:40:13 -06:00
delete this.components;
2023-10-04 08:18:09 -05:00
2023-11-29 12:40:13 -06:00
this.clear();
2023-11-29 12:40:13 -06:00
if (typeof this.stop === 'function')
this.stop();
},
2023-11-29 12:40:13 -06:00
up() { return [0,1].rotate(Math.deg2rad(this.angle));},
down() { return [0,-1].rotate(Math.deg2rad(this.angle));},
right() { return [1,0].rotate(Math.deg2rad(this.angle));},
left() { return [-1,0].rotate(Math.deg2rad(this.angle));},
2023-10-10 17:37:58 -05:00
make(level, data) {
2023-11-30 10:47:59 -06:00
level ??= Primum;
2023-10-04 17:57:37 -05:00
var obj = Object.create(this);
obj.make = undefined;
2023-11-30 10:47:59 -06:00
if (this.instances)
this.instances.push(obj);
obj.body = make_gameobject();
2023-11-29 12:40:13 -06:00
obj.components = {};
2023-09-29 13:16:59 -05:00
obj.objects = {};
obj.timers = [];
obj._ed = {
selectable: true,
2023-10-26 11:48:02 -05:00
check_dirty() {
this.urdiff = obj.json_obj();
this.dirty = !this.urdiff.empty;
if (!obj.level) return;
var lur = ur[obj.level.ur];
if (!lur) return;
var lur = lur.objects[obj.toString()];
var d = ediff(this.urdiff,lur);
if (!d || d.empty)
this.inst = true;
else
this.inst = false;
},
dirty: false,
2023-10-26 11:48:02 -05:00
inst: false,
urdiff: {},
namestr() {
var s = obj.toString();
if (this.dirty)
if (this.inst) s += "#";
else s += "*";
return s;
},
};
2023-11-29 12:40:13 -06:00
2023-10-05 17:30:17 -05:00
obj.ur = this.toString();
2023-11-30 10:47:59 -06:00
obj.reparent(level);
2023-09-20 13:33:11 -05:00
cmd(113, obj.body, obj); // set the internal obj reference to this obj
2023-09-08 12:35:06 -05:00
2023-10-04 17:57:37 -05:00
for (var prop in this) {
var p = this[prop];
2023-09-21 12:50:39 -05:00
if (typeof p !== 'object') continue;
if (typeof p.make === 'function') {
obj[prop] = p.make(obj);
2023-09-21 19:51:38 -05:00
obj.components[prop] = obj[prop];
}
2023-10-10 17:37:58 -05:00
};
2023-10-26 11:48:02 -05:00
if (this.objects)
2023-10-11 17:22:41 -05:00
obj.make_objs(this.objects);
2023-10-09 18:10:10 -05:00
2023-11-29 12:40:13 -06:00
Object.hide(obj, 'ur','body', 'components', 'objects', '_ed', 'level', 'timers');
Object.dainty_assign(obj, this);
2023-10-10 17:37:58 -05:00
obj.sync();
2023-10-04 17:57:37 -05:00
gameobject.check_registers(obj);
2023-10-02 17:03:01 -05:00
if (data)
Object.dainty_assign(obj,data);
if (typeof obj.warmup === 'function') obj.warmup();
2023-10-11 17:22:41 -05:00
if (Game.playing() && typeof obj.start === 'function') obj.start();
2023-09-08 12:35:06 -05:00
return obj;
},
2023-10-09 18:10:10 -05:00
make_objs(objs) {
for (var prop in objs) {
2023-11-29 12:40:13 -06:00
Log.warn(prop);
2023-10-11 17:22:41 -05:00
var newobj = this.spawn_from_instance(objs[prop]);
2023-10-09 18:10:10 -05:00
if (!newobj) continue;
this.rename_obj(newobj.toString(), prop);
}
},
2023-10-02 17:03:01 -05:00
rename_obj(name, newname) {
if (!this.objects[name]) {
Log.warn(`No object with name ${name}. Could not rename to ${newname}.`);
return;
}
2023-10-26 11:48:02 -05:00
if (name === newname) {
Object.hide(this, name);
return;
}
if (this.objects[newname])
2023-10-02 17:03:01 -05:00
return;
this.objects[newname] = this.objects[name];
this[newname] = this[name];
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];
},
add_component(comp) {
2023-11-29 12:40:13 -06:00
if (typeof comp.make !== 'function') return;
return {comp:comp.toString()};
},
2023-09-08 12:35:06 -05:00
register_hit(fn, obj) {
if (!obj)
obj = this;
Signal.obj_begin(fn, obj, this);
},
register_separate(fn, obj) {
if (!obj)
obj = this;
Signal.obj_separate(fn,obj,this);
},
}
2023-11-29 12:40:13 -06:00
gameobject.body = make_gameobject();
cmd(113,gameobject.body, gameobject);
Object.hide(gameobject, 'timescale');
gameobject.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 level.",
angle: "Rotation of this object, relative to its level.",
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 level.",
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.",
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.`,
gravity: 'True if this object should be affected by gravity.',
mass: `The higher the mass of the object, the less forces will affect it.`,
phys: `Set to 0, 1, or 2, representing static, kinematic, and dynamic.`,
worldpos: `Function returns the world position of the object.`,
set_worldpos: `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.`,
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.`,
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.',
in_air: `Return true if the object is in the air.`,
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.`,
level: "The entity this entity belongs to.",
2023-11-07 12:45:52 -06: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.',
};
/* Default objects */
var prototypes = {};
prototypes.ur_ext = ".jso";
prototypes.ur = {};
prototypes.save_gameobjects = function() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); };
2023-09-26 13:34:02 -05:00
/* Makes a new ur-type from disk. If the ur doesn't exist, it searches on the disk to create it. */
prototypes.from_file = function(file)
{
2023-09-26 13:34:02 -05:00
var urpath = file;
2023-09-26 08:37:19 -05:00
var path = urpath.split('.');
2023-09-26 13:34:02 -05:00
if (path.length > 1 && (path.at(-1) === path.at(-2))) {
urpath = path.slice(0,-1).join('.');
return prototypes.get_ur(urpath);
2023-09-26 13:34:02 -05:00
}
2023-10-04 17:57:37 -05:00
var upperur = gameobject;
2023-09-26 13:34:02 -05:00
2023-09-26 08:37:19 -05:00
if (path.length > 1) {
2023-11-20 07:49:14 -06:00
var upur = undefined;
2023-09-26 08:37:19 -05:00
var upperpath = path.slice(0,-1);
2023-11-20 07:49:14 -06:00
while (!upur && upperpath) {
upur = prototypes.get_ur(upperpath.join('/'));
upperpath = upperpath.slice(0,-1);
2023-09-26 13:34:02 -05:00
}
2023-11-20 07:49:14 -06:00
if (upur) upperur = upur;
2023-09-26 08:37:19 -05:00
}
2023-10-10 17:37:58 -05:00
var newur = {};
2023-10-04 17:57:37 -05:00
2023-09-26 13:34:02 -05:00
file = file.replaceAll('.','/');
var jsfile = prototypes.get_ur_file(urpath, prototypes.ur_ext);
2023-09-29 08:27:34 -05:00
var jsonfile = prototypes.get_ur_file(urpath, ".json");
2023-09-26 13:34:02 -05:00
var script = undefined;
var json = undefined;
2023-09-29 08:27:34 -05:00
if (jsfile) script = IO.slurp(jsfile);
2023-10-09 13:03:12 -05:00
try {
if (jsonfile) json = JSON.parse(IO.slurp(jsonfile));
} catch(e) {
2023-10-10 17:37:58 -05:00
Log.warn(`Unable to create json from ${jsonfile}`);
2023-10-09 13:03:12 -05:00
}
2023-09-26 13:34:02 -05:00
if (!json && !script) {
Log.warn(`Could not make ur from ${file}`);
return undefined;
}
if (script)
compile_env(script, newur, file);
2023-09-26 13:34:02 -05:00
json ??= {};
Object.merge(newur,json);
Object.entries(newur).forEach(function([k,v]) {
if (Object.isObject(v) && Object.isObject(upperur[k]))
v.__proto__ = upperur[k];
});
Object.values(newur).forEach(function(v) {
if (typeof v !== 'object') return;
if (!v.comp) return;
v.__proto__ = component[v.comp];
});
2023-10-04 17:57:37 -05:00
newur.__proto__ = upperur;
newur.instances = [];
2023-10-05 17:30:17 -05:00
Object.hide(newur, 'instances');
2023-10-04 17:57:37 -05:00
2023-09-26 13:34:02 -05:00
prototypes.list.push(urpath);
2023-09-26 08:37:19 -05:00
newur.toString = function() { return urpath; };
2023-09-26 13:34:02 -05:00
ur[urpath] = newur;
return newur;
}
prototypes.from_file.doc = "Create a new ur-type from a given script file.";
prototypes.list = [];
prototypes.list_ur = function()
{
var list = [];
function list_obj(obj, prefix)
{
prefix ??= "";
var list = [];
for (var e in obj) {
list.push(prefix + e);
list.concat(list_obj(obj[e], e + "."));
}
return list;
}
return list_obj(ur);
}
2023-09-29 08:27:34 -05:00
prototypes.ur2file = function(urpath)
{
return urpath.replaceAll('.', '/');
}
2023-09-26 13:34:02 -05:00
prototypes.file2ur = function(file)
2023-09-26 08:37:19 -05:00
{
2023-09-26 13:34:02 -05:00
file = file.strip_ext();
2023-09-26 08:37:19 -05:00
file = file.replaceAll('/','.');
2023-09-26 13:34:02 -05:00
return file;
2023-09-26 08:37:19 -05:00
}
2023-09-26 13:34:02 -05:00
/* Returns an ur, or makes it, for any given type of path
could be a file on a disk like ball/big.js
could be an ur path like ball.big
*/
prototypes.get_ur = function(name)
{
if (!name) {
Log.warn(`Can't get ur from an undefined.`);
return;
}
2023-09-26 13:34:02 -05:00
var urpath = name;
if (urpath.includes('/'))
urpath = prototypes.file2ur(name);
if (!prototypes.ur[urpath]) {
var ur = prototypes.from_file(urpath);
if (ur)
return ur;
else {
2023-09-26 08:37:19 -05:00
Log.warn(`Could not find prototype using name ${name}.`);
return undefined;
}
} else
2023-09-26 13:34:02 -05:00
return prototypes.ur[urpath];
}
2023-09-29 08:27:34 -05:00
prototypes.get_ur_file = function(path, ext)
{
var urpath = prototypes.ur2file(path);
var file = urpath + ext;
if (IO.exists(file)) return file;
file = urpath + "/" + path.split('.').at(-1) + ext;
if (IO.exists(file)) return file;
return undefined;
}
prototypes.generate_ur = function(path)
{
var ob = IO.glob("**" + prototypes.ur_ext);
2023-09-26 13:34:02 -05:00
ob = ob.concat(IO.glob("**.json"));
ob = ob.map(function(path) { return path.set_ext(""); });
ob.forEach(function(name) { prototypes.get_ur(name); });
}
var ur = prototypes.ur;
2023-10-04 17:57:37 -05:00
prototypes.resavi = function(ur, path)
{
if (!ur) return path;
if (path[0] === '/') return path;
2023-10-04 17:57:37 -05:00
var res = ur.replaceAll('.', '/');
var dir = path.dir();
if (res.startsWith(dir))
return path.base();
return path;
2023-10-04 17:57:37 -05:00
}
prototypes.resani = function(ur, path)
{
if (!path) return "";
if (!ur) return path;
if (path[0] === '/') return path.slice(1);
var res = ur.replaceAll('.', '/');
var restry = res + "/" + path;
while (!IO.exists(restry)) {
res = res.updir() + "/";
if (res === "/")
return path;
restry = res + path;
}
return restry;
2023-10-04 17:57:37 -05:00
}
prototypes.ur_dir = function(ur)
{
var path = ur.replaceAll('.', '/');
Log.warn(path);
Log.warn(IO.exists(path));
Log.warn(`${path} does not exist; sending ${path.dir()}`);
}
prototypes.ur_json = function(ur)
{
var path = ur.replaceAll('.', '/');
if (IO.exists(path))
path = path + "/" + path.name() + ".json";
else
path = path + ".json";
return path;
}
prototypes.ur_stem = function(ur)
{
var path = ur.replaceAll('.', '/');
if (IO.exists(path))
return path + "/" + path.name();
else
return path;
}
prototypes.ur_file_exts = ['.jso', '.json'];
prototypes.ur_folder = function(ur)
{
var path = ur.replaceAll('.', '/');
return IO.exists(path);
}
prototypes.ur_pullout_folder = function(ur)
{
if (!prototypes.ur_folder(ur)) return;
var stem = prototypes.ur_stem(ur);
/* prototypes.ur_file_exts.forEach(function(e) {
var p = stem + e;
if (IO.exists(p))
*/
}
prototypes.ur_pushin_folder = function(ur)
{
}