prosperon/scripts/editor.js

1804 lines
47 KiB
JavaScript
Raw Normal View History

/*
Editor-only variables on objects
selectable
*/
2023-04-28 12:49:18 -05:00
2024-03-18 08:16:25 -05:00
game.loadurs();
2024-04-03 17:17:32 -05:00
console.info(`window size: ${window.size}, render size: ${window.rendersize}`);
2024-03-19 23:01:31 -05:00
player[0].control(debug);
2024-04-17 08:57:45 -05:00
render.clear_color([35,60,92,255].map(x => x/255));
2024-04-04 17:28:11 -05:00
var show_frame = true;
2023-11-29 12:40:13 -06:00
var editor = {
toString() { return "editor"; },
grid_size: 100,
ruler_mark_px: 100,
grid_color: Color.green.alpha(0.3),
machine: undefined,
device_test: undefined,
selectlist: [],
scalelist: [],
rotlist: [],
camera: undefined,
edit_level: undefined, /* The current level that is being edited */
2023-10-09 18:10:10 -05:00
desktop: undefined, /* The editor desktop, where all editing objects live */
2023-09-11 17:09:21 -05:00
working_layer: 0,
get cursor() {
2024-04-03 00:44:08 -05:00
if (this.selectlist.length === 0 ) return input.mouse.worldpos();
2023-11-29 12:40:13 -06:00
return physics.com(this.selectlist.map(x => x.pos));
},
edit_mode: "basic",
2023-12-12 08:46:27 -06:00
get_this() { return this.edit_level; },
try_select() { /* nullify true if it should set selected to null if it doesn't find an object */
2024-04-03 00:44:08 -05:00
var go = physics.pos_query(input.mouse.worldpos());
return this.do_select(go);
},
2024-04-04 17:28:11 -05:00
do_select(obj) { /* select an object, if it is selectable given the current editor state */
2023-11-29 12:40:13 -06:00
if (!obj) return;
2023-12-24 11:50:01 -06:00
if (!obj._ed.selectable) return undefined;
2023-09-21 12:50:39 -05:00
if (obj.master !== this.edit_level) {
var testlevel = obj.master;
while (testlevel && testlevel.master !== world && testlevel.master !== this.edit_level && testlevel !== testlevel.master)
testlevel = testlevel.master;
return testlevel;
}
2023-09-21 12:50:39 -05:00
return obj;
if (this.working_layer > -1 && obj.draw_layer !== this.working_layer) return undefined;
return obj;
},
2023-09-11 17:09:21 -05:00
curpanel: undefined,
check_level_nested() {
if (this.edit_level.master) {
this.openpanel(gen_notify("Can't close a nested level. Save up to the root before continuing."));
return true;
}
return false;
},
programmode: false,
dup_objects(x) {
var objs = x.slice();
var duped = [];
2023-12-12 08:46:27 -06:00
objs.forEach(x => duped.push(x.dup()));
return duped;
},
sel_start: [],
mover(amt, snap) {
return function(go) { go.pos = go.pos.add(amt)};
},
2024-04-03 00:44:08 -05:00
step_amt() { return input.keyboard.down("shift") ? 10 : 1; },
on_grid(pos) {
2023-11-29 12:40:13 -06:00
return pos.every(function(x) { return x % editor.grid_size === 0; });
},
snapper(dir, grid) {
return function(go) {
go.pos = go.pos.add(dir.scale(grid/2));
go.pos = go.pos.map(function(x) { return Math.snap(x, grid) }, this);
}
},
key_move(dir) {
if (!editor.grabselect) return;
2024-04-03 00:44:08 -05:00
if (input.keyboard.down('ctrl'))
2023-11-29 12:40:13 -06:00
this.selectlist.forEach(this.snapper(dir.scale(1.01), editor.grid_size));
else
this.selectlist.forEach(this.mover(dir.scale(this.step_amt())));
},
/* Snapmode
0 No snap
1 Pixel snap
2 Grid snap
*/
snapmode: 0,
snapped_pos(pos) {
switch (this.snapmode) {
case 0:
return pos;
case 1:
return pos.map(function(x) { return Math.round(x); });
case 2:
return pos.map
}
},
unselect() {
editor.selectlist = [];
this.grabselect = [];
this.scalelist = [];
this.rotlist = [];
2023-09-11 17:09:21 -05:00
this.sel_comp = undefined;
},
2023-09-11 17:09:21 -05:00
sel_comp: undefined,
comp_info: false,
2023-09-11 17:09:21 -05:00
brush_obj: undefined,
camera_recalls: {},
camera_recall_stack: [],
camera_recall_store() {
this.camera_recall_stack.push({
pos:this.camera.pos,
zoom:this.camera.zoom
});
},
camera_recall_pop() {
Object.assign(this.camera, this.camera_recalls.pop());
},
camera_recall_clear() {
this.camera_recall_stack = [];
},
input_num_pressed(num) {
2024-04-03 00:44:08 -05:00
if (input.keyboard.down('ctrl')) {
this.camera_recalls[num] = {
pos:this.camera.pos,
zoom:this.camera.zoom
};
return;
}
if (num in this.camera_recalls)
Object.assign(this.camera, this.camera_recalls[num]);
},
zoom_to_bb(bb) {
var cwh = bbox.tocwh(bb);
2024-03-18 08:16:25 -05:00
var xscale = cwh.wh.x / window.width;
var yscale = cwh.wh.y / window.height;
var zoom = yscale > xscale ? yscale : xscale;
this.camera.pos = cwh.c;
this.camera.zoom = zoom*1.3;
},
z_start: undefined,
2023-09-11 17:09:21 -05:00
grabselect: undefined,
mousejoy: undefined,
joystart: undefined,
2023-10-09 18:10:10 -05:00
stash: undefined,
start_play_ed() {
2023-10-11 17:22:41 -05:00
this.stash = this.desktop.instance_obj();
world.clear();
global.mixin("config.js");
2024-03-15 11:21:36 -05:00
sim.play();
player[0].uncontrol(this);
player[0].control(limited_editor);
2024-01-03 14:26:42 -06:00
editor.cbs.forEach(cb => cb());
editor.cbs = [];
global.mixin("predbg.js");
2023-12-27 07:04:18 -06:00
console.warn(`starting game with ${this.dbg_ur}`);
editor.dbg_play = world.spawn(this.dbg_ur);
editor.dbg_play.pos = [0,0];
global.mixin("debug.js");
2023-10-09 18:10:10 -05:00
},
start_play() {
world.clear();
global.mixin("config.js");
2024-03-15 11:21:36 -05:00
sim.play();
player[0].uncontrol(this);
player[0].control(limited_editor);
2024-01-03 14:26:42 -06:00
editor.cbs.forEach(cb=>cb());
editor.cbs = [];
actor.spawn("game.js");
},
2024-01-03 14:26:42 -06:00
cbs: [],
2023-10-09 18:10:10 -05:00
enter_editor() {
2024-03-15 11:21:36 -05:00
sim.pause();
player[0].control(this);
player[0].uncontrol(limited_editor);
2024-01-03 14:26:42 -06:00
editor.cbs.push(Register.gui.register(editor.gui.bind(editor)));
editor.cbs.push(Register.draw.register(editor.draw.bind(editor)));
editor.cbs.push(Register.debug.register(editor.ed_debug.bind(editor)));
2024-04-03 17:17:32 -05:00
editor.cbs.push(Register.update.register(gui.controls.update, gui.controls));
2024-01-03 14:26:42 -06:00
this.desktop = world.spawn();
world.rename_obj(this.desktop.toString(), "desktop");
2023-10-11 17:22:41 -05:00
this.edit_level = this.desktop;
editor.edit_level._ed.selectable = false;
if (this.stash) {
this.desktop.make_objs(this.stash.objects);
Object.dainty_assign(this.desktop, this.stash);
}
this.selectlist = [];
editor.camera = world.spawn("scripts/camera2d.jso");
2023-12-24 11:50:01 -06:00
editor.camera._ed.selectable = false;
2024-03-20 16:48:03 -05:00
game.camera = editor.camera;
2023-10-09 18:10:10 -05:00
},
2024-03-20 16:48:03 -05:00
openpanel(panel) {
if (this.curpanel) {
this.curpanel.close();
player[0].uncontrol(this.curpanel);
}
this.curpanel = panel;
player[0].control(this.curpanel);
this.curpanel.open();
},
curpanels: [],
addpanel(panel) {
this.curpanels.push(panel);
panel.open();
},
cleanpanels(panel) {
this.curpanels = this.curpanels.filter(function(x) { return x.on; });
},
snapshots: [],
curlvl: {}, /* What the level currently looks like on file */
reset_undos() {
this.snapshots = [];
this.backshots = [];
},
snapshot() {
return; // TODO: Implement
2023-11-20 07:49:14 -06:00
var dif = this.edit_level.json_obj();
if (!dif) return;
if (this.snapshots.length !== 0) {
2023-12-18 06:45:27 -06:00
var ddif = ediff(dif, this.snapshots.last());
2023-11-20 07:49:14 -06:00
if (!ddif) return;
dif = ddif;
}
2023-11-20 07:49:14 -06:00
this.snapshots.push(dif);
this.backshots = [];
return;
this.snapshots.push(dif);
this.backshots = [];
this.backshots.push(diff(this.curlvl, cur));
this.curlvl = cur;
this.edit_level.check_dirty();
},
backshots: [], /* Redo snapshots */
restore_lvl(lvl) {
this.unselect();
this.edit_level.clear();
this.edit_level.load(lvl);
this.edit_level.check_dirty();
},
redo() {
2024-02-19 20:31:26 -06:00
if (Object.empty(this.backshots)) {
2024-02-25 17:31:48 -06:00
console.info("Nothing to redo.");
return;
}
this.snapshots.push(this.edit_level.save());
var dd = this.backshots.pop();
this.edit_level.clear();
this.edit_level.load(dd);
this.edit_level.check_dirty();
this.curlvl = dd;
},
undo() {
2024-02-19 20:31:26 -06:00
if (Object.empty(this.snapshots)) {
2024-02-25 17:31:48 -06:00
console.info("Nothing to undo.");
return;
}
this.unselect();
2023-11-20 07:49:14 -06:00
// this.backshots.push(this.edit_level.save());
var dd = this.snapshots.pop();
2023-11-20 07:49:14 -06:00
Object.dainty_assign(this.edit_level, dd);
2023-12-18 17:12:05 -06:00
this.edit_level.check_dirty();
},
restore_buffer() {
this.restore_level(this.filesnap);
},
save_current() {
if (!this.edit_level.file) {
this.openpanel(saveaspanel);
return;
}
},
draw_objects_names(obj,root,depth = 0){
if (!obj) return;
if (!obj.objects) return;
root = root ? root + "." : root;
Object.entries(obj.objects).forEach(function(x) {
var p = root + x[0];
2024-05-02 17:13:09 -05:00
render.text(p, x[1].this2screen(), 1, editor.color_depths[depth]);
editor.draw_objects_names(x[1], p, depth+1);
});
},
2023-09-11 17:09:21 -05:00
_sel_comp: undefined,
get sel_comp() { return this._sel_comp; },
set sel_comp(x) {
if (this._sel_comp)
player[0].uncontrol(this._sel_comp);
this._sel_comp = x;
2024-04-09 16:48:15 -05:00
if (this._sel_comp)
player[0].control(this._sel_comp);
},
time: 0,
color_depths: [],
draw() {
2023-12-15 12:45:09 -06:00
this.selectlist.forEach(x => {
if ('gizmo' in x && typeof x['gizmo'] === 'function' )
x.gizmo();
});
2024-03-18 08:16:25 -05:00
render.line(bbox.topoints(bbox.fromcwh([0,0],[game.width,game.height])).wrapped(1), Color.green);
/* Draw selection box */
if (this.sel_start) {
2024-04-03 00:44:08 -05:00
var endpos = input.mouse.worldpos();
var c = [];
c[0] = (endpos[0] - this.sel_start[0]) / 2;
c[0] += this.sel_start[0];
c[1] = (endpos[1] - this.sel_start[1]) / 2;
c[1] += this.sel_start[1];
var wh = [];
wh[0] = Math.abs(endpos[0] - this.sel_start[0]);
wh[1] = Math.abs(endpos[1] - this.sel_start[1]);
var bb = bbox.fromcwh(c,wh);
2024-03-19 23:01:31 -05:00
render.boundingbox(bb, Color.Editor.select.alpha(0.1));
render.line(bbox.topoints(bb).wrapped(1), Color.white);
}
},
2023-12-18 17:12:05 -06:00
gui() {
/* Clean out killed objects */
2024-04-04 17:28:11 -05:00
if (show_frame)
render.line(shape.box(window.rendersize.x, window.rendersize.y).wrapped(1).map(p => game.camera.world2view(p)), Color.yellow);
2024-04-03 00:44:08 -05:00
render.text([0,0], game.camera.world2view([0,0]));
render.text("WORKING LAYER: " + this.working_layer, [0,520]);
2024-04-09 16:48:15 -05:00
render.text("MODE: " + this.edit_mode, [0,520-render.font.linegap]);
2023-12-15 12:45:09 -06:00
if (this.comp_info && this.sel_comp)
2024-04-04 17:28:11 -05:00
render.text(input.print_pawn_kbm(this.sel_comp,false), [100,700],1);
2024-05-02 17:13:09 -05:00
render.cross(editor.edit_level.this2screen(),3,Color.blue);
var thiso = editor.get_this();
var clvl = thiso;
2023-10-02 17:03:01 -05:00
var lvlchain = [];
while (clvl !== world) {
2023-10-02 17:03:01 -05:00
lvlchain.push(clvl);
clvl = clvl.master;
2023-10-02 17:03:01 -05:00
}
lvlchain.push(clvl);
var colormap = ColorMap.Inferno;
editor.color_depths = [];
for (var i = 1; i > 0 ; i -= 0.1)
editor.color_depths.push(colormap.sample(i));
2023-10-02 17:03:01 -05:00
var ypos = 200;
var depth = 0;
2023-10-26 11:48:02 -05:00
var alldirty = false;
for (var lvl of lvlchain) {
2024-04-10 16:21:46 -05:00
if (!lvl._ed?.selectable) continue;
2023-10-26 11:48:02 -05:00
if (alldirty)
lvl._ed.dirty = true;
else {
2024-04-03 17:17:32 -05:00
if (!lvl._ed) continue;
2023-12-18 17:12:05 -06:00
lvl.check_dirty();
2023-10-26 11:48:02 -05:00
if (lvl._ed.dirty) alldirty = true;
}
}
2023-10-02 17:03:01 -05:00
lvlchain.reverse();
lvlchain.forEach(function(x,i) {
depth = i;
2023-12-18 17:12:05 -06:00
var lvlstr = x.namestr();
if (i === lvlchain.length-1) lvlstr += "[this]";
render.text(lvlstr, [0, ypos], 1, editor.color_depths[depth]);
2024-04-09 16:48:15 -05:00
ypos += render.font.linegap;
render.text("^^^^^^", [0,ypos],1);
ypos += render.font.linegap;
2023-10-02 17:03:01 -05:00
});
depth++;
render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
2024-04-09 08:01:54 -05:00
this.selectlist.forEach(function(x) {
2024-05-02 17:13:09 -05:00
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.this2screen());
render.cross(x.this2screen(), 10, Color.blue);
});
Object.entries(thiso.objects).forEach(function(x) {
2023-12-18 17:12:05 -06:00
var p = x[1].namestr();
2024-05-02 17:13:09 -05:00
render.text(p, x[1].this2screen().add([0,render.font.linegap]),1,editor.color_depths[depth]);
render.point(x[1].this2screen(),5,Color.blue.alpha(0.3));
render.point(x[1].this2screen(), 1, Color.red);
});
2024-04-11 17:17:49 -05:00
var mg = physics.pos_query(input.mouse.worldpos());
2023-09-29 13:16:59 -05:00
2024-04-10 16:21:46 -05:00
if (mg && mg._ed?.selectable) {
var p = mg.path_from(thiso);
2024-04-03 00:44:08 -05:00
render.text(p, input.mouse.screenpos(),1,Color.teal);
}
2023-12-20 17:20:29 -06:00
if (this.rotlist.length === 1)
2024-04-09 08:01:54 -05:00
render.text(Math.places(this.rotlist[0].angle, 3), input.mouse.screenpos(), 1, Color.teal);
2023-12-20 17:20:29 -06:00
if (this.selectlist.length === 1) {
var i = 1;
for (var key in this.selectlist[0].components) {
var selected = this.sel_comp === this.selectlist[0].components[key];
2023-10-10 17:37:58 -05:00
var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]";
2024-05-02 17:13:09 -05:00
render.text(str, this.selectlist[0].this2screen().add([0,-render.font.linegap*(i++)]));
}
if (this.sel_comp) {
if ('gizmo' in this.sel_comp) this.sel_comp.gizmo();
}
}
editor.edit_level.objects.forEach(function(obj) {
if (!obj._ed.selectable)
render.text("lock", obj,screenpos());
});
2024-04-17 08:57:45 -05:00
render.grid(1, editor.grid_size, editor.grid_color);
2024-04-03 00:44:08 -05:00
var startgrid = game.camera.view2world([-20,0]).map(function(x) { return Math.snap(x, editor.grid_size); });
var endgrid = game.camera.view2world([window.width, window.height]);
2024-03-18 08:16:25 -05:00
var w_step = Math.round(editor.ruler_mark_px/window.width * (endgrid.x-startgrid.x)/editor.grid_size)*editor.grid_size;
2023-11-29 12:40:13 -06:00
if (w_step === 0) w_step = editor.grid_size;
2024-03-18 08:16:25 -05:00
var h_step = Math.round(editor.ruler_mark_px/window.height * (endgrid.y-startgrid.y)/editor.grid_size)*editor.grid_size;
2023-11-29 12:40:13 -06:00
if (h_step === 0) h_step = editor.grid_size;
while(startgrid[0] <= endgrid[0]) {
2024-04-03 00:44:08 -05:00
render.text(startgrid[0], [game.camera.world2view([startgrid[0], 0])[0],0]);
startgrid[0] += w_step;
}
while(startgrid[1] <= endgrid[1]) {
2024-04-03 00:44:08 -05:00
render.text(startgrid[1], [0, game.camera.world2view([0, startgrid[1]])[1]]);
startgrid[1] += h_step;
}
if (this.curpanel && this.curpanel.on)
this.curpanel.gui();
this.curpanels.forEach(function(x) {
if (x.on) x.gui();
2023-12-24 09:14:46 -06:00
});
},
ed_debug() {
2024-03-19 23:01:31 -05:00
if (!debug.phys_drawing)
this.selectlist.forEach(function(x) { debug.draw_obj_phys(x); });
},
killring: [],
killcom: [],
lvl_history: [],
2024-03-01 11:45:06 -06:00
load(urstr) {
2024-04-03 17:17:32 -05:00
var mur = ur[urstr];
if (!mur) return;
var obj = editor.edit_level.spawn(mur);
obj.set_pos(input.mouse.worldpos());
this.selectlist = [obj];
},
load_prev() {
if (this.lvl_history.length === 0) return;
var file = this.lvl_history.pop();
this.edit_level = Level.loadfile(file);
this.unselect();
},
2023-09-26 13:34:02 -05:00
/* Checking to save an entity as a subtype. */
/* sub is the name of the (sub)type; obj is the object to save it as */
2024-03-01 11:45:06 -06:00
/* if saving subtype of 'empty', it appears on the top level */
2023-09-29 13:16:59 -05:00
saveas_check(sub, obj) {
2024-03-01 11:45:06 -06:00
if (!sub) {
console.warn(`Cannot save an object to an empty ur.`);
return;
}
if (!obj) {
console.warn(`Specify an obejct to save.`);
return;
}
if (obj.ur === 'empty') {
/* make a new type path */
if (Object.access(ur,sub)) {
console.warn(`Ur named ${sub} already exists.`);
2024-04-03 17:17:32 -05:00
return;
2024-03-01 11:45:06 -06:00
}
var file = `${sub}.json`;
io.slurpwrite(file, json.encode(obj.json_obj(),null,1));
ur[sub] = {
name: sub,
2024-04-03 17:17:32 -05:00
data: file,
2024-04-04 17:28:11 -05:00
fresh: json.decode(json.encode(obj))
2024-03-01 11:45:06 -06:00
}
obj.ur = sub;
return;
} else if (!sub.startswith(obj.ur)) {
console.warn(`Cannot make an ur of type ${sub} from an object with the ur ${obj.ur}`);
return;
}
var curur = Object.access(ur,sub);
2023-09-26 13:34:02 -05:00
if (curur) {
notifypanel.action = editor.saveas;
this.openpanel(gen_notify("Entity already exists with that name. Delete first."));
2023-09-26 13:34:02 -05:00
} else {
var path = sub.replaceAll('.', '/') + ".json";
2023-10-10 17:37:58 -05:00
var saveobj = obj.json_obj();
2024-02-25 17:31:48 -06:00
io.slurpwrite(path, JSON.stringify(saveobj,null,1));
if (obj === editor.edit_level) {
if (obj === editor.desktop) {
2024-04-03 17:17:32 -05:00
obj.clear();
var nobj = editor.edit_level.spawn(sub);
editor.selectlist = [nobj];
return;
}
editor.edit_level = editor.edit_level.master;
}
var t = obj.transform();
2023-09-26 13:34:02 -05:00
editor.unselect();
obj.kill();
obj = editor.edit_level.spawn(sub);
2023-09-29 13:16:59 -05:00
obj.pos = t.pos;
obj.angle = t.angle;
2023-09-26 13:34:02 -05:00
}
},
}
2024-03-01 11:45:06 -06:00
editor.new_object = function()
{
var obj = editor.edit_level.spawn();
obj.set_pos(input.mouse.worldpos());
2024-03-01 11:45:06 -06:00
this.selectlist = [obj];
return obj;
}
editor.new_object.doc = "Create an empty object.";
editor.new_from_img = function(path)
{
var o = editor.new_object();
o.add_component(component.sprite).path = path;
return o;
}
editor.inputs = {};
editor.inputs['C-b'] = function() {
if (this.selectlist.length !== 1) {
console.warn(`Can only bake a single object at a time.`);
return;
}
var obj = this.selectlist[0];
obj.components.forEach(function(c) {
if (typeof c.grow !== 'function') return;
c.grow(obj.scale);
c.sync?.();
});
2024-04-09 08:01:54 -05:00
obj.set_scale([1,1,1]);
}
2023-12-20 09:19:04 -06:00
editor.inputs.drop = function(str) {
2024-03-01 11:45:06 -06:00
str = str.slice(os.cwd().length+1);
2023-12-20 09:19:04 -06:00
if (!Resources.is_image(str)) {
console.warn("NOT AN IMAGE");
return;
}
2023-12-24 09:14:46 -06:00
2024-03-01 11:45:06 -06:00
if (this.selectlist.length === 0)
return editor.new_from_img(str);
2023-12-21 10:49:44 -06:00
2023-12-20 09:19:04 -06:00
if (this.sel_comp?.comp === 'sprite') {
this.sel_comp.path = str;
return;
}
2024-04-11 17:17:49 -05:00
var mg = physics.pos_query(input.mouse.worldpos());
2023-12-20 09:19:04 -06:00
if (!mg) return;
var img = mg.get_comp_by_name('sprite');
if (!img) return;
img[0].path = str;
}
2024-03-18 08:16:25 -05:00
editor.inputs.f9 = function() { os.capture( "capture.bmp", 0, 0, 500, 500); }
2023-11-22 03:51:43 -06:00
editor.inputs.release_post = function() {
editor.snapshot();
2024-04-04 17:28:11 -05:00
editor.selectlist?.forEach(x => x.check_dirty());
2023-12-27 10:34:14 -06:00
/* snap all objects to be pixel perfect */
2024-04-03 17:17:32 -05:00
game.all_objects(o => o.pos = o.pos.map(x => Math.round(x)), editor.edit_level);
};
editor.inputs['C-a'] = function() {
2024-02-19 20:31:26 -06:00
if (!Object.empty(editor.selectlist)) { editor.unselect(); return; }
editor.unselect();
editor.selectlist = editor.edit_level.objects.slice();
};
editor.inputs['C-a'].doc = "Select all objects.";
2023-09-21 19:51:38 -05:00
editor.inputs['C-`'] = function() { editor.openpanel(replpanel); }
editor.inputs['C-`'].doc = "Open or close the repl.";
editor.inputs.n = function() {
if (editor.selectlist.length !== 1) return;
var o = editor.try_select();
if (!o) return;
if (o === editor.selectlist[0]) return;
if (o.master !== editor.selectlist[0].master) return;
2024-04-09 16:48:15 -05:00
var tpos = editor.selectlist[0].get_pos(editor.selectlist[0].master);
tpos.x *= -1;
2024-04-09 16:48:15 -05:00
o.set_pos(tpos, o.master);
};
editor.inputs.n.doc = "Set the hovered object's position to mirror the selected object's position on the X axis."
editor.inputs['M-n'] = function()
{
if (editor.selectlist.length !== 1) return;
var o = editor.try_select();
if (!o) return;
if (o === editor.selectlist[0]) return;
if (o.master !== editor.selectlist[0].master) return;
var tpos = editor.selectlist[0].pos;
tpos.y *= -1;
o.pos = tpos;
};
editor.inputs.n.doc = "Set the hovered object's position to mirror the selected object's position on the Y axis."
/* Return if selected component. */
editor.inputs['h'] = function() {
var visible = true;
editor.selectlist.forEach(function(x) { if (x.visible) visible = false; });
editor.selectlist.forEach(function(x) { x.visible = visible; });
};
editor.inputs['h'].doc = "Toggle object hidden.";
editor.inputs['C-h'] = function() { world.objects.forEach(function(x) { x.visible = true; }); };
editor.inputs['C-h'].doc = "Unhide all objects.";
editor.inputs['C-e'] = function() { editor.openpanel(assetexplorer); };
editor.inputs['C-e'].doc = "Open asset explorer.";
2023-09-26 13:34:02 -05:00
editor.inputs['C-l'] = function() { editor.openpanel(entitylistpanel); };
2023-09-11 15:07:36 -05:00
editor.inputs['C-l'].doc = "Open list of spawned entities.";
2023-10-11 17:22:41 -05:00
editor.inputs['C-i'] = function() {
if (editor.selectlist.length !== 1) return;
objectexplorer.obj = editor.selectlist[0];
editor.openpanel(objectexplorer);
};
2023-09-11 15:07:36 -05:00
editor.inputs['C-i'].doc = "Open the object explorer for a selected object.";
2023-10-11 17:22:41 -05:00
editor.inputs['C-d'] = function() {
if (editor.selectlist.length === 0) return;
var duped = editor.dup_objects(editor.selectlist);
editor.unselect();
editor.selectlist = duped;
};
editor.inputs['C-d'].doc = "Duplicate all selected objects.";
editor.inputs['C-m'] = function() {
if (editor.sel_comp) {
if ('flipy' in editor.sel_comp)
editor.sel_comp.flipy = !editor.sel_comp.flipy;
return;
}
2023-11-15 16:42:39 -06:00
editor.selectlist.forEach(function(x) { x.mirror([0,1]);});
};
editor.inputs['C-m'].doc = "Mirror selected objects on the Y axis.";
editor.inputs.m = function() {
if (editor.sel_comp) {
if ('flipx' in editor.sel_comp)
editor.sel_comp.flipx = !editor.sel_comp.flipx;
return;
}
2023-12-24 09:14:46 -06:00
2024-04-09 08:01:54 -05:00
editor.selectlist.forEach(obj => obj.grow([-1,1,1]));
};
editor.inputs.m.doc = "Mirror selected objects on the X axis.";
editor.inputs.q = function() { editor.comp_info = !editor.comp_info; };
editor.inputs.q.doc = "Toggle help for the selected component.";
editor.inputs.f = function() {
2024-04-04 17:28:11 -05:00
return;
if (editor.selectlist.length === 0) return;
var bb = editor.selectlist[0].boundingbox();
editor.selectlist.forEach(function(obj) { bb = bbox.expand(bb, obj.boundingbox()); });
editor.zoom_to_bb(bb);
};
editor.inputs.f.doc = "Find the selected objects.";
editor.inputs['C-f'] = function() {
if (editor.selectlist.length !== 1) return;
2023-09-22 09:44:58 -05:00
editor.edit_level = editor.selectlist[0];
editor.unselect();
2023-11-20 07:49:14 -06:00
editor.reset_undos();
};
editor.inputs['C-f'].doc = "Tunnel into the selected level object to edit it.";
2024-04-11 17:17:49 -05:00
editor.inputs['M-f'] = function() {
if (editor.edit_level.master === world) return;
editor.edit_level = editor.edit_level.master;
editor.unselect();
editor.reset_undos();
};
2024-04-11 17:17:49 -05:00
editor.inputs['M-f'].doc = "Tunnel out of the level you are editing, saving it in the process.";
2024-04-09 08:01:54 -05:00
editor.inputs['C-r'] = function() { editor.selectlist.forEach(function(x) { x.rotate(-x.angle*2); }); }
editor.inputs['C-r'].doc = "Negate the selected's angle.";
editor.inputs.r = function() {
if (editor.sel_comp && 'angle' in editor.sel_comp) {
var relpos = input.mouse.worldpos().sub(editor.sel_comp.gameobject.pos);
return;
}
2024-04-09 08:01:54 -05:00
editor.rotlist = editor.selectlist;
};
editor.inputs.r.doc = "Rotate selected using the mouse while held down.";
editor.inputs.r.released = function() { editor.rotlist = []; }
2023-09-11 17:09:21 -05:00
2023-12-12 08:46:27 -06:00
editor.inputs.f5 = function() { editor.start_play_ed(); }
2023-10-09 18:10:10 -05:00
editor.inputs.f5.doc = "Start game from 'debug' if it exists; otherwise, from 'game'.";
2023-12-12 08:46:27 -06:00
editor.inputs.f6 = function() { editor.start_play(); }
editor.inputs.f6.doc = "Start game as if the player started it.";
editor.inputs['M-p'] = function() {
2024-03-15 11:21:36 -05:00
if (sim.playing())
sim.pause();
2024-03-15 11:21:36 -05:00
sim.step();
}
editor.inputs['M-p'].doc = "Do one time step, pausing if necessary.";
editor.inputs['C-M-p'] = function() {
2024-03-15 11:21:36 -05:00
if (!sim.playing()) {
editor.start_play_ed();
}
2024-02-25 17:31:48 -06:00
console.warn(`Starting edited level ...`);
};
editor.inputs['C-M-p'].doc = "Start game from currently edited level.";
editor.inputs['C-q'] = function() {
2023-10-04 08:18:09 -05:00
};
editor.inputs['C-q'].doc = "Quit simulation and return to editor.";
var rebinder = {};
rebinder.inputs = {};
rebinder.inputs.any = function(cmd) {
};
editor.inputs['C-space'] = function() {
};
editor.inputs['C-space'].doc = "Search to execute a specific command.";
editor.inputs['M-m'] = function() {
// player[0].control(rebinder);
};
editor.inputs['M-m'].doc = "Rebind a shortcut. Usage: M-m SHORTCUT TARGET";
editor.inputs['M-S-8'] = function() {
editor.camera_recall_pop();
};
editor.inputs['M-S-8'].doc = "Jump to last location.";
editor.inputs.escape = function() { editor.openpanel(quitpanel); }
editor.inputs.escape.doc = "Quit editor.";
editor.inputs['C-s'] = function() {
var saveobj = undefined;
2023-09-29 13:16:59 -05:00
if (editor.selectlist.length === 0) {
if (editor.edit_level === editor.desktop) {
saveaspanel.stem = editor.edit_level.ur.toString();
saveaspanel.obj = editor.edit_level;
editor.openpanel(saveaspanel);
return;
} else
saveobj = editor.edit_level;
} else if (editor.selectlist.length === 1)
saveobj = editor.selectlist[0];
2024-03-01 11:45:06 -06:00
saveobj.check_dirty();
if (!saveobj._ed.dirty) {
console.warn(`Object ${saveobj.full_path()} does not need saved.`);
return;
}
var savejs = saveobj.json_obj();
2024-05-02 17:13:09 -05:00
var tur = saveobj.ur;
2024-03-01 11:45:06 -06:00
if (!tur) {
console.warn(`Can't save object because it has no ur.`);
return;
}
if (!tur.data) {
io.slurpwrite(tur.text.set_ext(".json"), json.encode(savejs,null,1));
tur.data = tur.text.set_ext(".json");
2024-04-04 17:28:11 -05:00
} else {
2024-03-01 11:45:06 -06:00
var oldjs = json.decode(io.slurp(tur.data));
Object.merge(oldjs, savejs);
io.slurpwrite(tur.data, json.encode(oldjs,null,1));
}
2023-10-02 17:03:01 -05:00
2024-04-04 17:28:11 -05:00
Object.merge(tur.fresh, savejs);
2024-03-01 11:45:06 -06:00
saveobj.check_dirty();
2023-10-26 11:48:02 -05:00
2024-03-01 11:45:06 -06:00
// Object.values(saveobj.objects).forEach(function(x) { x.check_dirty(); });
2024-03-01 11:45:06 -06:00
return;
2024-03-18 08:16:25 -05:00
game.all_objects(function(x) {
if (typeof x !== 'object') return;
if (!('_ed' in x)) return;
if (x._ed.dirty) return;
x.revert();
2023-12-18 17:12:05 -06:00
x.check_dirty();
});
};
editor.inputs['C-s'].doc = "Save selected.";
2023-09-22 09:44:58 -05:00
editor.inputs['C-S'] = function() {
2023-09-26 13:34:02 -05:00
if (editor.selectlist.length !== 1) return;
2024-03-01 11:45:06 -06:00
if (this.selectlist[0].ur !== 'empty')
saveaspanel.stem = this.selectlist[0].ur + ".";
else
saveaspanel.stem = "";
saveaspanel.obj = this.selectlist[0];
editor.openpanel(saveaspanel);
};
2023-09-22 09:44:58 -05:00
editor.inputs['C-S'].doc = "Save selected as.";
editor.inputs['C-z'] = function() { editor.undo(); };
editor.inputs['C-z'].doc = "Undo the last change made.";
editor.inputs['C-S-z'] = function() { editor.redo(); };
editor.inputs['C-S-z'].doc = "Redo the last undo.";
editor.inputs.t = function() { editor.selectlist.forEach(function(x) { x._ed.selectable = false; }); };
editor.inputs.t.doc = "Lock selected objects to make them non selectable.";
editor.inputs['M-t'] = function() { editor.edit_level.objects.forEach(function(x) { x._ed.selectable = true; }); };
editor.inputs['M-t'].doc = "Unlock all objects in current level.";
2024-03-01 11:45:06 -06:00
editor.inputs['C-n'] = editor.new_object;
editor.inputs['C-o'] = function() {
editor.openpanel(openlevelpanel);
};
editor.inputs['C-o'].doc = "Open a level.";
editor.inputs['C-M-o'] = function() {
if (editor.selectlist.length === 1 && editor.selectlist[0].file) {
if (editor.edit_level._ed.dirty) return;
editor.load(editor.selectlist[0].file);
}
};
editor.inputs['C-M-o'].doc = "Revert opened level back to its disk form.";
editor.inputs['C-S-o'] = function() {
if (!editor.edit_level._ed.dirty)
editor.load_prev();
};
editor.inputs['C-S-o'].doc = "Open previous level.";
2023-09-11 15:07:36 -05:00
editor.inputs['C-y'] = function() {
texteditor.on_close = function() { editor.edit_level.script = texteditor.value;};
editor.openpanel(texteditor);
2023-09-29 08:27:34 -05:00
texteditor.value = "";
texteditor.start();
};
2023-09-11 15:07:36 -05:00
editor.inputs['C-y'].doc = "Open script editor for the level.";
2024-04-09 16:48:15 -05:00
editor.inputs['C-p'] = function() {
os.system("prosperon");
}
2023-09-11 15:07:36 -05:00
editor.inputs['M-y'] = function() { editor.programmode = !editor.programmode; };
editor.inputs['M-y'].doc = "Toggle program mode.";
editor.inputs.minus = function() {
2024-02-19 20:31:26 -06:00
if (!Object.empty(editor.selectlist)) {
editor.selectlist.forEach(function(x) { x.draw_layer--; });
return;
}
if (editor.working_layer > -1)
editor.working_layer--;
};
editor.inputs.minus.doc = "Go down one working layer, or, move selected objects down one layer.";
editor.inputs.plus = function() {
2024-02-19 20:31:26 -06:00
if (!Object.empty(editor.selectlist)) {
editor.selectlist.forEach(x => x.draw_layer++);
return;
}
if (editor.working_layer < 4)
editor.working_layer++;
};
editor.inputs.plus.doc = "Go up one working layer, or, move selected objects down one layer.";
editor.inputs['C-f1'] = function() { editor.edit_mode = "basic"; };
editor.inputs['C-f1'].doc = "Enter basic edit mode.";
editor.inputs['C-f2'] = function() { editor.edit_mode = "brush"; };
editor.inputs['C-f2'].doc = "Enter brush mode.";
editor.inputs.f2 = function() {
2023-12-24 09:14:46 -06:00
if (this.selectlist.length !== 1) return;
objectexplorer.obj = this.selectlist[0];
this.openpanel(objectexplorer);
};
editor.inputs.f2.doc = "Open configurations object.";
2023-12-24 09:14:46 -06:00
editor.inputs.f3 = function() {
if (this.selectlist.length !== 1) return;
this.openpanel(componentexplorer);
};
2024-04-03 00:44:08 -05:00
editor.inputs.lm = function() { editor.sel_start = input.mouse.worldpos(); };
editor.inputs.lm.doc = "Selection box.";
2023-09-11 17:09:21 -05:00
editor.inputs.lm.released = function() {
2024-04-03 00:44:08 -05:00
input.mouse.normal();
editor.unselect();
2023-09-11 17:09:21 -05:00
if (!editor.sel_start) return;
if (editor.sel_comp) {
editor.sel_start = undefined;
return;
}
var selects = [];
/* TODO: selects somehow gets undefined objects in here */
2024-04-03 00:44:08 -05:00
if (Vector.equal(input.mouse.worldpos(), editor.sel_start, 5)) {
2023-09-11 17:09:21 -05:00
var sel = editor.try_select();
if (sel) selects.push(sel);
} else {
2024-04-03 00:44:08 -05:00
var box = bbox.frompoints([editor.sel_start, input.mouse.worldpos()]);
2023-09-11 17:09:21 -05:00
2024-04-01 17:58:29 -05:00
physics.box_query(bbox.tocwh(box), function(entity) {
var obj = editor.do_select(entity);
if (obj) selects.push(obj);
2023-12-24 11:50:01 -06:00
});
2023-09-11 17:09:21 -05:00
}
this.sel_start = undefined;
selects = selects.flat();
selects = selects.unique();
2024-02-19 20:31:26 -06:00
if (Object.empty(selects)) return;
2023-09-11 17:09:21 -05:00
2024-04-03 00:44:08 -05:00
if (input.keyboard.down('shift')) {
2023-09-11 17:09:21 -05:00
selects.forEach(function(x) {
this.selectlist.push_unique(x);
}, this);
return;
}
2024-04-03 00:44:08 -05:00
if (input.keyboard.down('ctrl')) {
2023-09-11 17:09:21 -05:00
selects.forEach(function(x) {
2024-02-19 20:31:26 -06:00
delete this.selectlist[x.toString()];
2023-09-11 17:09:21 -05:00
}, this);
return;
}
editor.selectlist = [];
selects.forEach(function(x) {
if (x !== undefined)
this.selectlist.push(x);
}, this);
};
editor.inputs.rm = function() {
if (editor.brush_obj)
editor.brush_obj = undefined;
if (editor.sel_comp) {
editor.sel_comp = undefined;
return;
}
editor.unselect();
};
2023-09-29 08:27:34 -05:00
editor.try_pick = function()
{
editor.grabselect = [];
if (editor.sel_comp && 'pick' in editor.sel_comp)
2024-04-03 00:44:08 -05:00
return editor.sel_comp.pick(input.mouse.worldpos());
2023-09-29 08:27:34 -05:00
return editor.try_select();
2023-09-29 08:27:34 -05:00
}
2023-09-11 17:09:21 -05:00
editor.inputs.mm = function() {
if (editor.brush_obj) {
editor.selectlist = editor.dup_objects([editor.brush_obj]);
2024-04-03 00:44:08 -05:00
editor.selectlist[0].pos = input.mouse.worldpos();
2023-09-11 17:09:21 -05:00
editor.grabselect = editor.selectlist[0];
return;
}
var o = editor.try_pick();
if (!o) return;
// editor.selectlist = [o];
editor.grabselect = [o];
2023-09-11 17:09:21 -05:00
};
editor.inputs['C-mm'] = editor.inputs.mm;
2023-09-11 17:09:21 -05:00
editor.inputs['C-M-lm'] = function()
{
2024-04-03 00:44:08 -05:00
var go = physics.pos_query(input.mouse.worldpos());
if (!go) return;
editor.edit_level = go.master;
}
editor.inputs['C-M-mm'] = function() {
2024-04-03 00:44:08 -05:00
editor.mousejoy = input.mouse.screenpos();
editor.joystart = editor.camera.pos;
2023-09-11 17:09:21 -05:00
};
editor.inputs['C-M-rm'] = function() {
2024-04-03 00:44:08 -05:00
editor.mousejoy = input.mouse.screenpos();
editor.z_start = editor.camera.zoom;
2024-04-03 00:44:08 -05:00
input.mouse.disabled();
};
2023-09-11 17:09:21 -05:00
editor.inputs.rm.released = function() {
editor.mousejoy = undefined;
editor.z_start = undefined;
2024-04-03 00:44:08 -05:00
input.mouse.normal();
};
editor.inputs.mm.released = function () {
2024-04-03 00:44:08 -05:00
input.mouse.normal();
this.grabselect = [];
editor.mousejoy = undefined;
editor.joystart = undefined;
};
editor.inputs.mouse = {};
editor.inputs.mouse.move = function(pos, dpos)
{
if (editor.mousejoy) {
2023-12-19 17:28:45 -06:00
if (editor.z_start)
editor.camera.zoom -= dpos.y/500;
else if (editor.joystart)
2024-03-18 08:16:25 -05:00
editor.camera.pos = editor.camera.pos.sub(game.camera.dir_view2world(dpos));
}
2023-09-27 12:36:32 -05:00
editor.grabselect?.forEach(function(x) {
2024-03-18 08:16:25 -05:00
x.move(game.camera.dir_view2world(dpos));
2023-12-27 07:04:18 -06:00
x.sync();
2023-09-27 12:36:32 -05:00
});
2024-04-03 00:44:08 -05:00
var relpos = input.mouse.worldpos().sub(editor.cursor);
2024-04-09 08:01:54 -05:00
var lastpos = relpos.sub(dpos);
var dist = Vector.length(relpos.add(dpos)) - Vector.length(relpos);
var scalediff = 1+(dist/editor.scaleoffset);
editor.scalelist?.forEach(function(x) { x.grow(scalediff); });
2024-04-09 08:01:54 -05:00
var anglediff = Math.atan2(relpos.y, relpos.x) - Math.atan2(lastpos.y, lastpos.x);
editor.rotlist?.forEach(function(x) {
2024-04-09 08:01:54 -05:00
x.rotate(anglediff/(2*Math.PI));
if (input.keyboard.down('shift')) {
var rotate = Math.nearest(x.angle, 1/24) - x.angle;
x.rotate(rotate)
}
});
}
2023-09-11 17:09:21 -05:00
editor.inputs.mouse.scroll = function(scroll)
{
scroll.y *= -1;
2024-03-18 08:16:25 -05:00
editor.camera.move(game.camera.dir_view2world(scroll.scale(-3)));
}
2023-09-29 08:27:34 -05:00
editor.inputs.mouse['C-scroll'] = function(scroll)
{
editor.camera.zoom += scroll.y/100;
}
2024-04-03 00:44:08 -05:00
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.delete = function() {
this.selectlist.forEach(x => x.kill());
this.unselect();
};
editor.inputs.delete.doc = "Delete selected objects.";
editor.inputs['S-d'] = editor.inputs.delete;
2023-09-26 08:37:19 -05:00
editor.inputs['C-k'] = editor.inputs.delete;
2024-04-09 08:01:54 -05:00
editor.inputs['C-u'] = function() { this.selectlist.forEach(x => x.revert()); };
editor.inputs['C-u'].doc = "Revert selected objects back to their prefab form.";
editor.inputs['M-u'] = function() {
this.selectlist.forEach(function(x) {
x.unique = true;
});
};
editor.inputs['M-u'].doc = "Make selected objects unique.";
editor.inputs['C-S-g'] = function() { editor.openpanel(groupsaveaspanel); };
editor.inputs['C-S-g'].doc = "Save selected objects as a new level.";
editor.inputs.g = function() {
if (editor.selectlist.length === 0) {
var o = editor.try_pick();
if (!o) return;
editor.selectlist = [o];
}
2023-12-21 10:49:44 -06:00
if (editor.sel_comp) {
if ('pick' in editor.sel_comp) {
2024-04-03 00:44:08 -05:00
editor.grabselect = [editor.sel_comp.pick(input.mouse.worldpos())];
2023-12-21 10:49:44 -06:00
return;
}
if ('pos' in editor.sel_comp) {
var comp = editor.sel_comp;
var o = {
pos: editor.sel_comp.pos,
move(d) { comp.pos = comp.pos.add(comp.gameobject.dir_world2this(d)); },
sync: comp.sync.bind(comp),
2023-12-21 10:49:44 -06:00
};
editor.grabselect = [o];
return;
}
}
if (editor.sel_comp && 'pick' in editor.sel_comp) {
2024-04-03 00:44:08 -05:00
var o = editor.sel_comp.pick(input.mouse.worldpos());
if (o) editor.grabselect = [o];
return;
}
editor.grabselect = editor.selectlist.slice();
};
editor.inputs.g.doc = "Move selected objects.";
2024-04-03 00:44:08 -05:00
editor.inputs.g.released = function() { editor.grabselect = []; input.mouse.normal(); };
editor.inputs.up = function() { this.key_move([0,1]); };
editor.inputs.up.rep = true;
editor.inputs.left = function() { this.key_move([-1,0]); };
editor.inputs.left.rep = true;
editor.inputs.right = function() { this.key_move([1,0]); };
editor.inputs.right.rep = true;
editor.inputs.down = function() { this.key_move([0,-1]); };
editor.inputs.down.rep = true;
editor.inputs.tab = function() {
2023-12-15 12:45:09 -06:00
if (!(this.selectlist.length === 1)) return;
if (!this.selectlist[0].components) return;
var sel = this.selectlist[0].components;
if (!this.sel_comp)
this.sel_comp = sel.nth(0);
else {
var idx = sel.findIndex(this.sel_comp) + 1;
if (idx >= Object.keys(sel).length)
2023-09-11 17:09:21 -05:00
this.sel_comp = undefined;
else
this.sel_comp = sel.nth(idx);
}
};
editor.inputs.tab.doc = "Cycle through selected object's components.";
editor.inputs['C-g'] = function() {
2023-09-11 17:09:21 -05:00
if (!this.selectlist) return;
this.selectlist = this.dup_objects(this.selectlist);
editor.inputs.g();
};
editor.inputs['C-g'].doc = "Duplicate selected objects, then move them.";
editor.inputs['M-g'] = function()
{
if (this.sel_comp && 'pick_all' in this.sel_comp)
this.grabselect = this.sel_comp.pick_all();
}
editor.inputs['M-g'].doc = "Move all.";
editor.inputs['C-lb'] = function() {
2024-04-03 00:44:08 -05:00
editor.grid_size -= input.keyboard.down('shift') ? 10 : 1;
2023-11-29 12:40:13 -06:00
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'].rep = true;
2024-04-03 00:44:08 -05:00
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'].rep = true;
editor.inputs['C-c'] = function() {
this.killring = [];
this.killcom = [];
2023-11-29 12:40:13 -06:00
this.killcom = physics.com(this.selectlist.map(x=>x.pos));
this.selectlist.forEach(function(x) {
2023-12-24 09:14:46 -06:00
this.killring.push(x.instance_obj());
},this);
};
editor.inputs['C-c'].doc = "Copy selected objects to killring.";
editor.inputs['C-x'] = function() {
2023-09-26 08:37:19 -05:00
editor.inputs['C-c'].call(editor);
this.selectlist.forEach(function(x) { x.kill(); });
editor.unselect();
};
editor.inputs['C-x'].doc = "Cut objects to killring.";
2023-09-26 08:37:19 -05:00
editor.inputs['C-v'] = function() {
this.unselect();
this.killring.forEach(function(x) {
editor.selectlist.push(editor.edit_level.spawn(x));
});
this.selectlist.forEach(function(x) {
x.pos = x.pos.sub(this.killcom).add(this.cursor);
},this);
};
editor.inputs['C-v'].doc = "Pull objects from killring to world.";
editor.inputs.char = function(c) {
if (c === '0') {
this.camera.pos = [0,0];
this.camera.zoom = 1;
}
};
var brushmode = {};
brushmode.inputs = {};
2023-09-26 08:37:19 -05:00
brushmode.inputs.lm = function() { editor.inputs['C-v'].call(editor); };
brushmode.inputs.lm.doc = "Paste selected brush.";
brushmode.inputs.b = function() {
if (editor.brush_obj) {
2023-09-11 17:09:21 -05:00
editor.brush_obj = undefined;
return;
}
if (editor.selectlist.length !== 1) return;
editor.brush_obj = editor.seliectlist[0];
editor.unselect();
};
brushmode.inputs.b.doc = "Clear brush, or set a new one.";
var compmode = {};
compmode.inputs = {};
compmode.inputs['C-c'] = function() {}; /* Simply a blocker */
compmode.inputs['C-x'] = function() {};
editor.scalelist = [];
editor.inputs.s = function() {
2024-04-09 08:01:54 -05:00
editor.scaleoffset = Vector.length(input.mouse.worldpos().sub(editor.cursor));
editor.scalelist = [];
if (editor.sel_comp) {
if (!('scale' in editor.sel_comp)) return;
2024-04-09 08:01:54 -05:00
editor.scalelist.push(editor.sel_comp);
return;
}
2024-04-09 08:01:54 -05:00
editor.scalelist = editor.selectlist;
};
editor.inputs.s.doc = "Scale selected.";
editor.inputs.s.released = function() { this.scalelist = []; };
2023-09-11 17:09:21 -05:00
2023-09-21 12:50:39 -05:00
var replpanel = Object.copy(inputpanel, {
title: "",
2023-09-21 12:50:39 -05:00
closeonsubmit:false,
2023-10-04 17:57:37 -05:00
wh: [700,300],
pos: [50,50],
2024-04-03 17:17:32 -05:00
anchor: [0,1],
padding: [0,0],
2023-10-09 13:03:12 -05:00
scrolloffset: [0,0],
2023-10-04 17:57:37 -05:00
2023-09-21 12:50:39 -05:00
guibody() {
2023-10-09 13:03:12 -05:00
this.win.selectable = true;
2024-04-03 17:17:32 -05:00
var log = console.transcript;
2023-10-04 17:57:37 -05:00
return [
2023-10-09 13:03:12 -05:00
Mum.text({str:log, anchor:[0,0], offset:[0,-300].sub(this.scrolloffset), selectable: true}),
2023-11-07 12:45:52 -06:00
Mum.text({str:this.value,color:Color.green, offset:[0,-290], caret: this.caret})
2023-10-04 17:57:37 -05:00
];
2023-09-21 12:50:39 -05:00
},
2023-10-04 17:57:37 -05:00
prevmark:-1,
prevthis:[],
2023-09-21 12:50:39 -05:00
action() {
2023-10-04 17:57:37 -05:00
if (!this.value) return;
this.prevthis.unshift(this.value);
this.prevmark = -1;
2023-10-02 17:03:01 -05:00
var ecode = "";
var repl_obj = editor.get_this();
2023-10-02 17:03:01 -05:00
ecode += `var $ = repl_obj.objects;`;
for (var key in repl_obj.objects)
ecode += `var ${key} = editor.edit_level.objects['${key}'];`;
2023-09-21 12:50:39 -05:00
ecode += this.value;
2024-03-14 09:33:15 -05:00
say(this.value);
2023-09-21 12:50:39 -05:00
this.value = "";
2023-10-04 17:57:37 -05:00
this.caret = 0;
2023-10-02 17:03:01 -05:00
var ret = function() {return eval(ecode);}.call(repl_obj);
if (typeof ret === 'object') ret = json.encode(ret,null,1);
2024-04-12 13:53:00 -05:00
if (typeof ret !== 'undefined') say(ret);
2023-09-21 12:50:39 -05:00
},
2023-10-09 13:03:12 -05:00
resetscroll() {
this.scrolloffset.y = 0;
},
2023-09-21 12:50:39 -05:00
});
2023-10-04 17:57:37 -05:00
replpanel.inputs = Object.create(inputpanel.inputs);
replpanel.inputs.block = true;
replpanel.inputs.lm = function()
{
2024-04-03 00:44:08 -05:00
var mg = physics.pos_query(input.mouse.worldpos());
if (!mg) return;
var p = mg.path_from(editor.get_this());
this.value = p;
this.caret = this.value.length;
}
2023-10-09 13:03:12 -05:00
replpanel.inputs.tab = function() {
this.resetscroll();
if (!this.value) return;
2023-10-09 13:03:12 -05:00
var obj = globalThis;
var keys = [];
var keyobj = this.value.tolast('.');
var o = this.value.tolast('.');
var stub = this.value.fromlast('.');
var replobj = (editor.selectlist.length === 1) ? "editor.selectlist[0]" : "editor.edit_level";
if (this.value.startswith("this."))
keyobj = keyobj.replace("this", replobj);
if (!this.value.includes('.')) keys.push("this");
if (eval(`typeof ${keyobj.tofirst('.')}`) === 'object' && eval(`typeof ${keyobj.replace('.', '?.')}`) === 'object')
2023-10-09 13:03:12 -05:00
obj = eval(keyobj);
else if (this.value.includes('.')){
2024-03-14 09:33:15 -05:00
say(`${this.value} is not an object.`);
2023-10-09 13:03:12 -05:00
return;
}
for (var k in obj)
keys.push(k)
for (var k in editor.get_this())
keys.push(k);
2023-10-09 13:03:12 -05:00
var comp = "";
if (stub)
comp = input.tabcomplete(stub, keys);
2023-10-09 13:03:12 -05:00
else if (!this.value.includes('.'))
comp = input.tabcomplete(o, keys);
2023-10-09 13:03:12 -05:00
else
comp = input.tabcomplete("",keys);
2023-10-09 13:03:12 -05:00
if (stub)
this.value = o + '.' + comp;
else if (this.value.endswith('.'))
this.value = o + '.' + comp;
else
this.value = comp;
this.caret = this.value.length;
keys = keys.sort();
keys = keys.map(function(x) {
if (typeof obj[x] === 'function')
2024-02-25 17:31:48 -06:00
return esc.color(Color.Apple.orange) + x + esc.reset;
2023-10-09 13:03:12 -05:00
if (Object.isObject(obj[x]))
2024-02-25 17:31:48 -06:00
return esc.color(Color.Apple.purple) + x + esc.reset;
2023-10-09 13:03:12 -05:00
if (Array.isArray(obj[x]))
2024-02-25 17:31:48 -06:00
return esc.color(Color.Apple.green) + x + esc.reset;
2023-10-09 13:03:12 -05:00
return x;
});
if (keys.length > 1)
2024-03-14 09:33:15 -05:00
say(keys.join(', '));
2023-10-09 13:03:12 -05:00
};
2023-10-04 17:57:37 -05:00
replpanel.inputs['C-p'] = function()
{
if (this.prevmark >= this.prevthis.length) return;
this.prevmark++;
this.value = this.prevthis[this.prevmark];
this.inputs['C-e'].call(this);
2023-10-04 17:57:37 -05:00
}
replpanel.inputs['C-n'] = function()
{
this.prevmark--;
if (this.prevmark < 0) {
this.prevmark = -1;
this.value = "";
} else
this.value = this.prevthis[this.prevmark];
this.inputs['C-e'].call(this);
2023-10-04 17:57:37 -05:00
}
2023-10-09 13:03:12 -05:00
replpanel.inputs.mouse = {};
replpanel.inputs.mouse.scroll = function(scroll)
{
if (!this.win.selected) return;
this.scrolloffset.y += scroll.y;
if (this.scrolloffset.y < 0) this.scrolloffset.y = 0;
}
replpanel.inputs.mm = function() { this.mm = true; };
replpanel.inputs.mm.released = function() { this.mm = false; };
replpanel.inputs.mouse.move = function(pos,dpos)
{
if (this.mm)
this.scrolloffset.y -= dpos.y;
}
2023-10-09 13:03:12 -05:00
replpanel.inputs.up = function()
{
this.scrolloffset.y += 40;
}
replpanel.inputs.up.rep = true;
replpanel.inputs.down = function()
{
this.scrolloffset.y -= 40;
if (this.scrolloffset.y < 0) this.scrolloffset.y = 0;
}
replpanel.inputs.down.rep = true;
replpanel.inputs.pgup = function()
{
this.scrolloffset.y += 300;
}
replpanel.inputs.pgup.rep = true;
replpanel.inputs.pgdown = function()
{
this.scrolloffset.y -= 300;
if (this.scrolloffset.y < 0) this.scrolloffset.y = 0;
}
replpanel.inputs.pgdown.rep = true;
var objectexplorer = Object.copy(inputpanel, {
title: "object explorer",
2023-09-11 17:09:21 -05:00
obj: undefined,
previous: [],
start() {
this.previous = [];
},
goto_obj(obj) {
if (obj === this.obj) return;
this.previous.push(this.obj);
this.obj = obj;
},
2023-10-11 17:22:41 -05:00
prev_obj() {
this.obj = this.previous.pop();
},
guibody() {
2023-10-11 17:22:41 -05:00
var items = [];
2023-12-24 09:14:46 -06:00
items.push(Mum.text({str:"Examining " + this.obj.toString() + " entity"}));
items.push(Mum.text({str: JSON.stringify(this.obj,undefined,1)}));
2023-10-26 11:48:02 -05:00
return items;
var n = 0;
var curobj = this.obj;
while (curobj) {
n++;
curobj = curobj.__proto__;
}
n--;
curobj = this.obj.__proto__;
while (curobj) {
2023-10-11 17:22:41 -05:00
items.push(Mum.text({str:curobj.toString(), action:this.goto_obj(curobj)}));
curobj = curobj.__proto__;
}
2024-02-19 20:31:26 -06:00
if (!Object.empty(this.previous))
2023-12-18 06:45:27 -06:00
items.push(Mum.text({str:"prev: " + this.previous.last(), action: this.prev_obj}));
Object.getOwnPropertyNames(this.obj).forEach(key => {
var descriptor = Object.getOwnPropertyDescriptor(this.obj, key);
if (!descriptor) return;
var hidden = !descriptor.enumerable;
var writable = descriptor.writable;
var configurable = descriptor.configurable;
if (!descriptor.configurable) return;
if (hidden) return;
var name = (hidden ? "[hidden] " : "") + key;
var val = this.obj[key];
switch (typeof val) {
case 'object':
if (val) {
2023-10-11 17:22:41 -05:00
items.push(Mum.text({str:name}));
items.push(Mum.text({str:val.toString(), action: this.goto_obj.bind(val)}));
}
break;
case 'function':
2023-10-11 17:22:41 -05:00
items.push(Mum.text({str:name}));
items.push(Mum.text({str:"function"}));
break;
default:
2024-03-18 08:16:25 -05:00
items.push(Mum.text({str:name}));
items.push(Mum.text({str:val.toString()}));
break;
}
});
2023-10-11 17:22:41 -05:00
items.push(Mum.text({str:"Properties that can be pulled in ..."}));
var pullprops = [];
for (var key in this.obj.__proto__) {
if (!this.obj.hasOwn(key)) {
if (typeof this.obj[key] === 'object' || typeof this.obj[key] === 'function') continue;
pullprops.push(key);
}
}
pullprops = pullprops.sort();
pullprops.forEach(function(key) {
2023-10-11 17:22:41 -05:00
items.push(Mum.text({str:key}));
});
2023-10-11 17:22:41 -05:00
return items;
},
});
var openlevelpanel = Object.copy(inputpanel, {
2023-09-11 15:07:36 -05:00
title: "open entity",
action() {
editor.load(this.value);
},
assets: [],
allassets: [],
2023-10-04 08:18:09 -05:00
mumlist: {},
submit_check() {
if (this.assets.length === 0) return false;
this.value = this.assets[0];
return true;
},
start() {
this.allassets = ur._list.sort();
this.assets = this.allassets.slice();
this.caret = 0;
var click_ur = function(btn) {
this.value = btn.str;
this.keycb();
this.submit();
};
click_ur = click_ur.bind(this);
2023-10-04 08:18:09 -05:00
this.mumlist = [];
this.assets.forEach(function(x) {
this.mumlist[x] = Mum.text({str:x, action:click_ur, color: Color.blue, hovered: {color:Color.red}, selectable:true});
}, this);
2023-10-04 08:18:09 -05:00
},
2023-10-04 08:18:09 -05:00
keycb() {
2023-10-26 11:48:02 -05:00
if(this.value)
this.assets = this.allassets.filter(x => x.startswith(this.value));
else
this.assets = this.allassets.slice();
2023-10-04 08:18:09 -05:00
for (var m in this.mumlist)
this.mumlist[m].hide = true;
this.assets.forEach(function(x) {
this.mumlist[x].hide = false;
}, this);
},
guibody() {
var a = [Mum.text({str:this.value,color:Color.green, caret:this.caret})];
2023-10-04 17:57:37 -05:00
var b = a.concat(Object.values(this.mumlist));
return Mum.column({items:b, offset:[0,-10]});
},
});
2024-03-01 11:45:06 -06:00
/* Should set stem to the ur path folloed by a '.' before opening */
var saveaspanel = Object.copy(inputpanel, {
2024-03-01 11:45:06 -06:00
get title() {
var full = this.stem ? this.stem : "";
return `save level as: ${full}.`;
},
action() {
2024-03-01 11:45:06 -06:00
var saveur = this.value;
if (this.stem) saveur = this.stem + saveur;
editor.saveas_check(saveur, this.obj);
},
});
var groupsaveaspanel = Object.copy(inputpanel, {
title: "group save as",
action() { editor.groupsaveas(editor.selectlist, this.value); }
});
var quitpanel = Object.copy(inputpanel, {
title: "really quit?",
2024-03-15 11:21:36 -05:00
action() { os.quit(); },
guibody () {
return Mum.text({str: "Really quit?"});
},
});
var allfiles = [];
2023-12-20 09:19:04 -06:00
allfiles.push(Resources.scripts, Resources.images, Resources.sounds);
allfiles = allfiles.flat();
var assetexplorer = Object.copy(openlevelpanel, {
title: "asset explorer",
extensions: allfiles,
closeonsubmit: false,
allassets:[],
action() {
if (editor.sel_comp && 'asset' in editor.sel_comp)
editor.sel_comp.asset = this.value;
else
editor.viewasset(this.value);
},
});
2023-12-24 09:14:46 -06:00
var componentexplorer = Object.copy(inputpanel, {
title: "component menu",
assets: ['sprite', 'model', 'edge2d', 'polygon2d', 'circle2d'],
click(name) {
if (editor.selectlist.length !== 1) return;
editor.selectlist[0].add_component(component[name]);
},
guibody() {
return componentexplorer.assets.map(x => Mum.text({str:x, action:this.click, color: Color.blue, hovered:{Color:Color.red}, selectable:true}));
},
});
var entitylistpanel = Object.copy(inputpanel, {
title: "Level object list",
level: {},
start() {
this.master = editor.edit_level;
},
});
var limited_editor = {};
limited_editor.inputs = {};
limited_editor.inputs['C-p'] = function()
{
2024-03-15 11:21:36 -05:00
if (sim.playing())
sim.pause();
else
2024-03-15 11:21:36 -05:00
sim.play();
}
limited_editor.inputs['M-p'] = function()
{
2024-03-15 11:21:36 -05:00
sim.pause();
sim.step();
}
limited_editor.inputs['C-q'] = function()
{
world.clear();
global.mixin("editorconfig.js");
global.mixin("dbgret.js");
2023-10-11 17:22:41 -05:00
2023-10-09 18:10:10 -05:00
editor.enter_editor();
}
/* This is used for editing during a paused game */
var limited_editing = {};
limited_editing.inputs = {};
/* This is the editor level & camera - NOT the currently edited level, but a level to hold editor things */
2024-03-15 11:21:36 -05:00
sim.pause();
2024-03-18 08:16:25 -05:00
window.editor = true;
2024-03-19 23:01:31 -05:00
debug.draw_phys = true;
return {
editor
}