Editor camera zoom and pan; fix mouse input signals; tex editor to own file; fix grid drawing

This commit is contained in:
John Alanbrook 2023-09-12 22:19:46 +00:00
parent ef95b233e2
commit e6ef147c40
14 changed files with 679 additions and 681 deletions

View file

@ -570,8 +570,6 @@ void draw_arrow(struct cpVect start, struct cpVect end, struct rgba color, int c
void draw_grid(float width, float span, struct rgba color)
{
cpVect offset = cam_pos();
offset.x -= mainwin.width/2;
offset.y -= mainwin.height/2;
offset = cpvmult(offset, 1/cam_zoom());
float ubo[4];

View file

@ -126,7 +126,7 @@ struct gameobject *js2go(JSValue v) {
return id2go(js2int(v));
}
struct sprite *js2sprite(JSValue v) { return id2go(js2int(v)); }
struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); }
void *js2ptr(JSValue v) {
void *p;
@ -1100,7 +1100,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 126:
mainwin.height = js2int(argv[2]);
mainwin.height = js2int(argv[1]);
break;
case 127:

View file

@ -29,6 +29,7 @@ JSValue jsaxis;
JSValue jsany;
JSValue jsmouse;
JSValue jspos;
JSValue jsmove;
cpVect mouse_pos = {0, 0};
cpVect mouse_delta = {0, 0};
@ -84,25 +85,35 @@ char *mb2str(int btn)
{
switch(btn) {
case 0:
return "lmouse";
return "lm";
break;
case 1:
return "rmouse";
return "rm";
break;
case 2:
return "mmouse";
return "mm";
break;
}
return "NULLMOUSE";
}
void input_mouse(int btn, int state)
void input_mouse(int btn, int state, uint32_t mod)
{
char out[16] = {0};
snprintf(out, 16, "%s%s%s%s",
mod & SAPP_MODIFIER_CTRL ? "C-" : "",
mod & SAPP_MODIFIER_ALT ? "M-" : "",
mod & SAPP_MODIFIER_SUPER ? "S-" : "",
mb2str(btn)
);
JSValue argv[3];
argv[0] = JS_NewString(js, "emacs");
argv[1] = input2js(mb2str(btn));
argv[1] = JS_NewString(js, out);
argv[2] = jsinputstate[state];
script_callee(pawn_callee, 3, argv);
JS_FreeValue(js, argv[0]);
JS_FreeValue(js, argv[1]);
}
void input_mouse_move(float x, float y, float dx, float dy)
@ -114,10 +125,12 @@ void input_mouse_move(float x, float y, float dx, float dy)
JSValue argv[3];
argv[0] = jsmouse;
argv[1] = jspos;
argv[1] = jsmove;
argv[2] = vec2js(mouse_pos);
script_callee(pawn_callee, 3, argv);
argv[3] = vec2js(mouse_delta);
script_callee(pawn_callee, 4, argv);
JS_FreeValue(js, argv[2]);
JS_FreeValue(js, argv[3]);
}
void input_mouse_scroll(float x, float y)
@ -218,6 +231,7 @@ void input_init() {
jsany = str2js("any");
jsmouse = str2js("mouse");
jspos = str2js("pos");
jsmove = str2js("move");
for (int i = 0; i < 512; i++)
key_states[i] = INPUT_UP;
@ -371,11 +385,6 @@ void cursor_show() { sapp_show_mouse(1); }
int action_down(int key) { return key_states[key] == INPUT_DOWN; }
int action_up(int key) { return key_states[key] == INPUT_UP; }
int want_quit() {
return mquit;
}
void quit() {
exit(0);
mquit = 1;
sapp_quit();
}

View file

@ -27,7 +27,7 @@ void cursor_hide();
void cursor_show();
void set_mouse_mode(int mousemode);
void input_mouse(int btn, int state);
void input_mouse(int btn, int state, uint32_t mod);
void input_mouse_move(float x, float y, float dx, float dy);
void input_mouse_scroll(float x, float y);
void input_btn(int btn, int state, uint32_t mod);
@ -39,7 +39,6 @@ int action_down(int key);
void register_pawn(struct callee c);
void register_gamepad(struct callee c);
int want_quit();
void quit();
#endif

View file

@ -87,6 +87,8 @@ void gif_rec_start(int w, int h, int cpf, int bitdepth)
void gif_rec_end(char *path)
{
if (!gif.rec) return;
MsfGifResult gif_res = msf_gif_end(&gif_state);
if (gif_res.data) {
FILE *f = fopen(path, "wb");
@ -432,6 +434,7 @@ void full_2d_pass(struct window *window)
call_nk_gui();
nuke_end();
}
void full_3d_pass(struct window *window)
@ -479,11 +482,8 @@ void openglRender(struct window *window) {
sg_apply_bindings(&crt_post.bind);
sg_draw(0,6,1);
sg_end_pass();
sg_commit();
debug_newframe();

View file

@ -199,8 +199,9 @@ void c_frame()
gameobjects_cleanup();
}
void c_clean() {};
void c_clean() {
gif_rec_end("crash.gif");
};
void c_event(const sapp_event *e)
{
@ -224,11 +225,11 @@ void c_event(const sapp_event *e)
break;
case SAPP_EVENTTYPE_MOUSE_UP:
input_mouse(e->mouse_button, INPUT_UP);
input_mouse(e->mouse_button, INPUT_UP, e->modifiers);
break;
case SAPP_EVENTTYPE_MOUSE_DOWN:
input_mouse(e->mouse_button, INPUT_DOWN);
input_mouse(e->mouse_button, INPUT_DOWN, e->modifiers);
break;
case SAPP_EVENTTYPE_CHAR:

View file

@ -2,15 +2,11 @@
Editor-only variables on objects
selectable
*/
prototypes.generate_ur('.');
/* This is the editor level & camera - NOT the currently edited level, but a level to hold editor things */
var editor_level = gameobject.make(Primum);
var editor_camera = camera2d.make(editor_level);
editor_camera.save = false;
Game.view_camera(editor_camera);
var editor_level = Primum.spawn(ur.arena);
var editor_camera = Game.camera;
var editor_config = {
grid_size: 100,
@ -46,9 +42,8 @@ var editor = {
/* Tries to select id */
do_select(go) {
var obj = go >= 0 ? Game.object(go) : undefined;
if (obj)
Log.warn(obj);
if (!obj || !obj.selectable) return undefined;
return obj;
if (this.working_layer > -1 && obj.draw_layer !== this.working_layer) return undefined;
@ -170,22 +165,13 @@ var editor = {
var objs = x.slice();
var duped = [];
objs.forEach(function(x,i) {
if (x.file) {
var newlevel = this.edit_level.addfile(x.file);
newlevel.pos = x.pos;
newlevel.angle = x.angle;
duped.push(newlevel);
} else {
var newobj = this.edit_level.spawn(x.__proto__);
duped.push(newobj);
objs.forEach(function(x) {
var newobj = this.edit_level.spawn(x.ur);
dainty_assign(newobj, x);
newobj.pos = x.pos;
newobj.angle = x.angle;
dainty_assign(newobj, x.prop_obj());
}
},this);
duped.forEach(function(x) { delete duped.varname; });
duped.push(newobj);
} ,this);
return duped.flat();
},
@ -226,22 +212,6 @@ var editor = {
}
},
input_up_pressed() {
this.key_move([0,1]);
},
input_up_rep() {
this.input_up_pressed();
},
input_left_pressed() {
this.key_move([-1,0]);
},
input_left_rep() {
this.input_left_pressed();
},
key_move(dir) {
if (Keys.ctrl())
this.selectlist.forEach(this.snapper(dir.scale(1.01), editor_config.grid_size));
@ -249,22 +219,6 @@ var editor = {
this.selectlist.forEach(this.mover(dir.scale(this.step_amt())));
},
input_right_pressed() {
this.key_move([1,0]);
},
input_right_rep() {
this.input_right_pressed();
},
input_down_pressed() {
this.key_move([0,-1]);
},
input_down_rep() {
this.input_down_pressed();
},
/* Snapmode
0 No snap
1 Pixel snap
@ -344,16 +298,10 @@ var editor = {
this.camera.zoom = zoom*1.3;
},
z_start: 1,
z_start: undefined,
grabselect: undefined,
mousejoy: [0,0],
joystart: [0,0],
input_mmouse_released() {
Mouse.normal();
this.grabselect = undefined;
},
mousejoy: undefined,
joystart: undefined,
stash: "",
@ -369,29 +317,6 @@ var editor = {
moveoffsets: [],
// TODO: Unify so this works
/* input_g_pressed() {
if (this.sel_comp) {
if ('pos' in this.sel_comp)
this.moveoffset = this.sel_comp.pos.sub(screen2world(Mouse.pos));
return;
}
},
*/
input_g_down() {
if (Keys.alt()) return;
if (this.sel_comp) {
this.sel_comp.pos = this.moveoffset.add(screen2world(Mouse.pos));
return;
}
if (this.moveoffsets.length === 0) return;
this.selectlist.forEach(function(x,i) {
x.pos = this.moveoffsets[i].add(screen2world(Mouse.pos));
}, this);
},
scaleoffset: 0,
startscales: [],
selected_com: [0,0],
@ -588,7 +513,6 @@ var editor = {
GUI.text("WORKING LAYER: " + this.working_layer, [0,520]);
GUI.text("MODE: " + this.edit_mode, [0,500]);
Debug.point(world2screen(this.edit_level.pos), 5, Color.yellow);
if (this.cursor) {
Debug.point(World2screen(this.cursor), 5, Color.green);
@ -678,7 +602,7 @@ var editor = {
gui_img("icons/icons8-lock-16.png", world2screen(obj.pos));
});
Debug.draw_grid(2, editor_config.grid_size/editor_camera.zoom, editor_config.grid_color);
Debug.draw_grid(1, editor_config.grid_size/editor_camera.zoom, editor_config.grid_color);
var startgrid = screen2world([-20,Window.height]).map(function(x) { return Math.snap(x, editor_config.grid_size); }, this);
var endgrid = screen2world([Window.width, 0]);
@ -764,12 +688,11 @@ var editor = {
load(file) {
if (this.edit_level) this.lvl_history.push(this.edit_level.ur);
this.edit_level.kill();
// this.edit_level.kill();
// this.edit_level = Level.loadfile(file);
// this.curlvl = this.edit_level.save();
this.edit_level = Primum.spawn(prototypes.get_ur(file));
Log.warn(`Loaded file ${file}`);
Log.warn(this.edit_level.pos);
Primum.spawn(prototypes.get_ur(file));
this.unselect();
},
@ -1279,13 +1202,6 @@ editor.inputs.rm = function() {
return;
}
if (Keys.ctrl() && Keys.alt()) {
editor.mousejoy = Mouse.pos;
editor.z_start = editor.camera.zoom;
Mouse.disabled();
return;
}
if (editor.brush_obj)
editor.brush_obj = undefined;
@ -1298,17 +1214,6 @@ editor.inputs.rm = function() {
};
editor.inputs.mm = function() {
if (Keys.ctrl() && Keys.alt()) {
editor.mousejoy = Mouse.pos;
editor.joystart = editor.camera.pos;
return;
}
if (Keys.shift() && Keys.ctrl()) {
editor.cursor = find_com(editor.selectlist);
return;
}
if (editor.brush_obj) {
editor.selectlist = editor.dup_objects([editor.brush_obj]);
editor.selectlist[0].pos = screen2world(Mouse.pos);
@ -1325,10 +1230,11 @@ editor.inputs.mm = function() {
}
var grabobj = editor.try_select();
if (Array.isArray(grabobj)) {
/* if (Array.isArray(grabobj)) {
editor.selectlist = grabobj;
return;
}
*/
editor.grabselect = undefined;
if (!grabobj) return;
@ -1345,32 +1251,52 @@ editor.inputs.mm = function() {
editor.moveoffset = editor.grabselect.pos.sub(screen2world(Mouse.pos));
};
editor.inputs['C-mm'] = editor.inputs.mm;
editor.inputs.rm.down = function() {
if (Keys.ctrl() && Keys.alt())
this.camera.zoom = this.z_start * (1 + (Mouse.pos[1] - this.mousejoy[1])/500);
editor.inputs['C-M-mm'] = function() {
editor.mousejoy = Mouse.pos;
editor.joystart = editor.camera.pos;
};
editor.inputs.mm.down = function() {
if (Keys.shift() && !Keys.ctrl()) {
this.cursor = Mouse.worldpos;
return;
editor.inputs['C-M-rm'] = function() {
editor.mousejoy = Mouse.pos;
editor.z_start = editor.camera.zoom;
Mouse.disabled();
};
editor.inputs['C-S-mm'] = function() { editor.cursor = find_com(editor.selectlist); };
editor.inputs.rm.released = function() {
editor.mousejoy = undefined;
editor.z_start = undefined;
Mouse.normal();
};
editor.inputs['S-mm'] = function() { this.cursor = Mouse.worldpos; };
editor.inputs.mm.released = function () {
Mouse.normal();
this.grabselect = undefined;
editor.mousejoy = undefined;
editor.joystart = undefined;
};
editor.inputs.mouse = {};
editor.inputs.mouse.move = function(pos, dpos)
{
if (editor.mousejoy) {
if (editor.z_start)
editor.camera.zoom += dpos.y/500;
else if (editor.joystart)
editor.camera.pos = editor.camera.pos.add(dpos.scale(editor.camera.zoom).mapc(mult,[-1,1]));
}
if (Keys.alt() && Keys.ctrl()) {
this.camera.pos = this.joystart.add(Mouse.pos.sub(this.mousejoy).mapc(mult, [-1,1]).scale(editor_camera.zoom));
return;
}
if (!this.grabselect) return;
if (this.grabselect) {
if ('pos' in this.grabselect)
this.grabselect.pos = this.moveoffset.add(screen2world(Mouse.pos));
else
this.grabselect.set(this.selectlist[0].world2this(this.moveoffset.add(screen2world(Mouse.pos))));
};
editor.inputs.mm.released = function () { Mouse.normal(); };
}
}
editor.inputs['C-M-S-lm'] = function() { editor.selectlist[0].set_center(screen2world(Mouse.pos)); };
editor.inputs['C-M-S-lm'].doc = "Set world center to mouse position.";
@ -1399,11 +1325,42 @@ 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 (this.sel_comp) {
if ('pos' in this.sel_comp)
this.moveoffset = this.sel_comp.pos.sub(screen2world(Mouse.pos));
return;
}
editor.selectlist.forEach(function(x,i) { editor.moveoffsets[i] = x.pos.sub(screen2world(Mouse.pos)); } );
};
editor.inputs.g.doc = "Move selected objects.";
editor.inputs.g.released = function() { editor.moveoffsets = []; };
editor.inputs.g.down = function() {
if (this.sel_comp) {
this.sel_comp.pos = this.moveoffset.add(screen2world(Mouse.pos));
return;
}
if (this.moveoffsets.length === 0) return;
this.selectlist.forEach(function(x,i) {
x.pos = this.moveoffsets[i].add(screen2world(Mouse.pos));
}, this);
};
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() {
if (!this.selectlist.length === 1) return;
if (!this.selectlist[0].components) return;
@ -1666,341 +1623,7 @@ function proto_children(name) {
return children;
}
var texteditor = clone(inputpanel, {
title: "text editor",
_cursor:0, /* Text cursor: [char,line] */
get cursor() { return this._cursor; },
set cursor(x) {
if (x > this.value.length)
x = this.value.length;
if (x < 0)
x = 0;
this._cursor = x;
this.line = this.get_line();
},
submit() {},
line: 0,
killring: [],
undos: [],
startbuffer: "",
savestate() {
this.undos.push(this.value.slice());
},
popstate() {
if (this.undos.length === 0) return;
this.value = this.undos.pop();
this.cursor = this.cursor;
},
copy(start, end) {
return this.value.slice(start,end);
},
delete_line(p) {
var ls = this.line_start(p);
var le = this.line_end(p)+1;
this.cut_span(ls,le);
this.to_line_start();
},
line_blank(p) {
var ls = this.line_start(p);
var le = this.line_end(p);
var line = this.value.slice(ls, le);
if (line.search(/[^\s]/g) === -1)
return true;
else
return false;
},
get_line() {
var line = 0;
for (var i = 0; i < this.cursor; i++)
if (this.value[i] === "\n")
line++;
return line;
},
start() {
this.cursor = 0;
this.startbuffer = this.value.slice();
},
get dirty() {
return this.startbuffer !== this.value;
},
gui() {
GUI.text_cursor(this.value, [100,700],1,this.cursor+1);
GUI.text("C" + this.cursor + ":::L" + this.line + ":::" + (this.dirty ? "DIRTY" : "CLEAN"), [100,100], 1);
},
insert_char(char) {
this.value = this.value.slice(0,this.cursor) + char + this.value.slice(this.cursor);
this.cursor++;
},
input_enter_pressrep() {
var white = this.line_starting_whitespace(this.cursor);
this.insert_char('\n');
for (var i = 0; i < white; i++)
this.insert_char(" ");
},
input_text(char) {
if (Keys.ctrl() || Keys.alt()) return;
this.insert_char(char);
this.keycb();
},
input_backspace_pressrep() {
this.value = this.value.slice(0,this.cursor-1) + this.value.slice(this.cursor);
this.cursor--;
},
line_starting_whitespace(p) {
var white = 0;
var l = this.line_start(p);
while (this.value[l] === " ") {
white++;
l++;
}
return white;
},
cut_span(start, end) {
if (end < start) return;
this.savestate();
var ret = this.value.slice(start,end);
this.value = this.value.slice(0,start) + this.value.slice(end);
if (start > this.cursor)
return ret;
this.cursor -= ret.length;
return ret;
},
next_word(pos) {
var v = this.value.slice(pos+1).search(/[^\w]\w/g);
if (v === -1) return pos;
return pos + v + 2;
},
prev_word(pos) {
while (this.value.slice(pos,pos+2).search(/[^\w]\w/g) === -1 && pos > 0)
pos--;
return pos+1;
},
end_of_word(pos) {
var l = this.value.slice(pos).search(/\w[^\w]/g);
return l+pos;
},
get inset() {
return this.cursor - this.value.prev('\n', this.cursor) - 1;
},
line_start(p) {
return this.value.prev('\n', p)+1;
},
line_end(p) {
return this.value.next('\n', p);
},
next_line(p) {
return this.value.next('\n',p)+1;
},
prev_line(p) {
return this.line_start(this.value.prev('\n', p));
},
to_line_start() {
this.cursor = this.value.prev('\n', this.cursor)+1;
},
to_line_end() {
var p = this.value.next('\n', this.cursor);
if (p === -1)
this.to_file_end();
else
this.cursor = p;
},
line_width(pos) {
var start = this.line_start(pos);
var end = this.line_end(pos);
if (end === -1)
end = this.value.length;
return end-start;
},
to_file_end() { this.cursor = this.value.length; },
to_file_start() { this.cursor = 0; },
desired_inset: 0,
});
texteditor.inputs = {};
texteditor.inputs['C-s'] = function() {
editor.edit_level.script = texteditor.value;
editor.save_current();
texteditor.startbuffer = texteditor.value.slice();
};
texteditor.inputs['C-s'].doc = "Save script to file.";
texteditor.inputs['C-u'] = function() { this.popstate(); };
texteditor.inputs['C-u'].doc = "Undo.";
texteditor.inputs['C-q'] = function() {
var ws = this.prev_word(this.cursor);
var we = this.end_of_word(this.cursor)+1;
var find = this.copy(ws, we);
var obj = editor.edit_level.varname2obj(find);
if (obj) {
editor.unselect();
editor.selectlist.push(obj);
}
};
texteditor.inputs['C-q'].doc = "Select object of selected word.";
texteditor.inputs['C-o'] = function() {
this.insert_char('\n');
this.cursor--;
};
texteditor.inputs['C-o'].doc = "Insert newline.";
texteditor.inputs['C-o'].rep = true;
texteditor.inputs['M-o'] = function() {
while (this.line_blank(this.next_line(this.cursor)))
this.delete_line(this.next_line(this.cursor));
while (this.line_blank(this.prev_line(this.cursor)))
this.delete_line(this.prev_line(this.cursor));
};
texteditor.inputs['M-o'].doc = "Delete surround blank lines.";
texteditor.inputs['C-d'] = function () { this.value = this.value.slice(0,this.cursor) + this.value.slice(this.cursor+1); };
texteditor.inputs['C-d'].doc = "Delete character.";
texteditor.inputs['M-d'] = function() { this.cut_span(this.cursor, this.end_of_word(this.cursor)+1); };
texteditor.inputs['M-d'].doc = "Delete word.";
texteditor.inputs['C-a'] = function() {
this.to_line_start();
this.desired_inset = this.inset;
};
texteditor.inputs['C-a'].doc = "To start of line.";
texteditor.inputs['C-y'] = function() {
if (this.killring.length === 0) return;
this.insert_char(this.killring.pop());
};
texteditor.inputs['C-y'].doc = "Insert from killring.";
texteditor.inputs['C-e'] = function() {
this.to_line_end();
this.desired_inset = this.inset;
};
texteditor.inputs['C-e'].doc = "To line end.";
texteditor.inputs['C-k'] = function() {
if (this.cursor === this.value.length-1) return;
var killamt = this.value.next('\n', this.cursor) - this.cursor;
var killed = this.cut_span(this.cursor-1, this.cursor+killamt);
this.killring.push(killed);
};
texteditor.inputs['C-k'].doc = "Kill from cursor to end of line.";
texteditor.inputs['M-k'] = function() {
var prevn = this.value.prev('\n', this.cursor);
var killamt = this.cursor - prevn;
var killed = this.cut_span(prevn+1, prevn+killamt);
this.killring.push(killed);
this.to_line_start();
};
texteditor.inputs['M-k'].doc = "Kill entire line the cursor is on.";
texteditor.inputs['C-b'] = function() {
this.cursor--;
this.desired_inset = this.inset;
};
texteditor.inputs['C-b'].rep = true;
texteditor.inputs['M-b'] = function() {
this.cursor = this.prev_word(this.cursor-2);
this.desired_inset = this.inset;
};
texteditor.inputs['M-b'].rep = true;
texteditor.inputs['C-f'] = function() {
this.cursor++;
this.desired_inset = this.inset;
};
texteditor.inputs['C-f'].rep = true;
texteditor.inputs['M-f'] = function() {
this.cursor = this.next_word(this.cursor);
this.desired_inset = this.inset;
};
texteditor.inputs['M-f'].rep = true;
texteditor.inputs['C-p'] = function() {
if (this.cursor === 0) return;
this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.prev_line(this.cursor);
var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew);
};
texteditor.inputs['C-p'].rep = true;
texteditor.inputs['M-p'] = function() {
while (this.line_blank(this.cursor))
this.cursor = this.prev_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.prev_line(this.cursor);
};
texteditor.inputs['M-p'].doc = "Go up to next line with text on it.";
texteditor.inputs['M-p'].rep = true;
texteditor.inputs['C-n'] = function() {
if (this.cursor === this.value.length-1) return;
if (this.value.next('\n', this.cursor) === -1) {
this.to_file_end();
return;
}
this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.next_line(this.cursor);
var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew);
};
texteditor.inputs['C-n'].rep = true;
texteditor.inputs['M-n'] = function() {
while (this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
};
texteditor.inputs['M-n'].doc = "Go down to next line with text on it.";
texteditor.inputs['M-n'].rep = true;
load("scripts/textedit.js");
var objectexplorer = copy(inputpanel, {
title: "object explorer",
@ -2429,4 +2052,3 @@ if (IO.exists("editor.config"))
editor.edit_level = editor_level;
Game.stop();
Game.render();

View file

@ -214,16 +214,18 @@ var Register = {
},
kbm_input(mode, btn, state, ...args) {
if (btn === 'lmouse') btn = 'lm';
if (btn === 'rmouse') btn = 'rm';
if (btn === 'mmouse') btn = 'mm';
if (state === 'released') {
btn = btn.split('-').last;
}
switch(mode) {
case "emacs":
Player.players[0].raw_input(btn, state, ...args);
break;
case "mouse":
Player.players[0].mouse_input(btn, state, ...args);
break;
};
},
@ -556,16 +558,17 @@ Register.update.register(Game.exec, Game);
//load("scripts/level.js");
load("scripts/entity.js");
var World = Object.create(gameobject);
var Primum = World;
var objects = [];
Primum.tag = "PRIMUM";
Primum.selectable = false;
Primum.ur = { tag: "Primum" };
Primum.objects = [];
World.remove_child = function(child) {
objects.remove(child);
this.objects.remove(child);
}
World.add_child = function(child) {
@ -633,29 +636,6 @@ function save_game_configs() {
load("scripts/physics.js");
var camera2d = gameobject.clone("camera2d", {
phys: gameobject.bodytype.kinematic,
speed: 300,
get zoom() { return this._zoom; },
set zoom(x) {
if (x <= 0) return;
this._zoom = x;
cmd(62, this._zoom);
},
_zoom: 1.0,
speedmult: 1.0,
selectable: false,
view2world(pos) {
return pos.mapc(mult, [1,-1]).add([-Window.width,Window.height].scale(0.5)).scale(this.zoom).add(this.pos);
},
world2view(pos) {
return pos.sub(this.pos).scale(1/this.zoom).add(Window.dimensions.scale(0.5));
},
});
Game.view_camera = function(cam)
{
@ -663,156 +643,9 @@ Game.view_camera = function(cam)
cmd(61, Game.camera.body);
}
Game.view_camera(camera2d.make(World));
Game.view_camera(Primum.spawn(ur.camera2d));
win_make(Game.title, Game.resolution[0], Game.resolution[1]);
/* Default objects */
var prototypes = {};
prototypes.ur = {};
prototypes.load_all = function()
{
if (IO.exists("proto.json"))
prototypes = JSON.parse(IO.slurp("proto.json"));
for (var key in prototypes) {
if (key in gameobjects)
dainty_assign(gameobjects[key], prototypes[key]);
else {
/* Create this gameobject fresh */
Log.info("Making new prototype: " + key + " from " + prototypes[key].from);
var newproto = gameobjects[prototypes[key].from].clone(key);
gameobjects[key] = newproto;
for (var pkey in newproto)
if (typeof newproto[pkey] === 'object' && newproto[pkey] && 'clone' in newproto[pkey])
newproto[pkey] = newproto[pkey].clone();
dainty_assign(gameobjects[key], prototypes[key]);
}
}
}
prototypes.save_gameobjects = function() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); };
prototypes.from_file = function(file)
{
if (!IO.exists(file)) {
Log.error(`File ${file} does not exist.`);
return;
}
var newobj = gameobject.clone(file, {});
var script = IO.slurp(file);
newobj.$ = {};
var json = {};
if (IO.exists(file.name() + ".json")) {
json = JSON.parse(IO.slurp(file.name() + ".json"));
Object.assign(newobj.$, json.$);
delete json.$;
}
compile_env(`var self = this; var $ = self.$; ${script}`, newobj, file);
dainty_assign(newobj, json);
file = file.replaceAll('/', '.');
var path = file.name().split('.');
var nested_access = function(base, names) {
for (var i = 0; i < names.length; i++)
base = base[names[i]] = base[names[i]] || {};
return base;
};
var a = nested_access(ur, path);
a.tag = file.name();
prototypes.list.push(a.tag);
a.type = newobj;
a.instances = [];
newobj.ur = a;
return a;
}
prototypes.from_file.doc = "Create a new ur-type from a given script file.";
prototypes.list = [];
prototypes.from_obj = function(name, obj)
{
var newobj = gameobject.clone(name, obj);
prototypes.ur[name] = {
tag: name,
type: newobj
};
return prototypes.ur[name];
}
prototypes.load_config = function(name)
{
if (!prototypes.ur[name])
prototypes.ur[name] = gameobject.clone(name);
Log.warn(`Made new ur of name ${name}`);
return prototypes.ur[name];
}
prototypes.list_ur = function()
{
var list = [];
function list_obj(obj, prefix)
{
prefix ??= "";
var list = [];
for (var e in obj) {
list.push(prefix + e);
Log.warn("Descending into " + e);
list.concat(list_obj(obj[e], e + "."));
}
return list;
}
return list_obj(ur);
}
prototypes.get_ur = function(name)
{
if (!prototypes.ur[name]) {
if (IO.exists(name + ".js"))
prototypes.from_file(name + ".js");
prototypes.load_config(name);
return prototypes.ur[name];
} else
return prototypes.ur[name];
}
prototypes.from_obj("polygon2d", {
polygon2d: polygon2d.clone(),
});
prototypes.from_obj("edge2d", {
edge2d: bucket.clone(),
});
prototypes.from_obj("sprite", {
sprite: sprite.clone(),
});
prototypes.generate_ur = function(path)
{
var ob = IO.glob("*.js");
ob = ob.concat(IO.glob("**/*.js"));
ob = ob.filter(function(str) { return !str.startsWith("scripts"); });
ob.forEach(function(name) {
if (name === "game.js") return;
if (name === "play.js") return;
prototypes.from_file(name);
});
}
var ur = prototypes.ur;
Window.width = 1280;
Window.height = 720;

View file

@ -339,7 +339,7 @@ var gameobject = {
};
obj.fullpath = function() {
return `${obj.level.fullpath()}.${obj.name}`;
return obj.ur.tag;
};
obj.deflock('toString');
obj.defc('from', this.name);
@ -450,3 +450,180 @@ var gameobject = {
var locks = ['height', 'width', 'visible', 'body', 'controlled', 'selectable', 'save', 'velocity', 'angularvelocity', 'alive', 'boundingbox', 'name', 'scale', 'angle', 'properties', 'moi', 'relpos', 'relangle', 'up', 'down', 'right', 'left', 'bodytype', 'gizmo', 'pos'];
locks.forEach(x => gameobject.obscure(x));
/* Default objects */
var prototypes = {};
prototypes.ur = {};
prototypes.load_all = function()
{
if (IO.exists("proto.json"))
prototypes = JSON.parse(IO.slurp("proto.json"));
for (var key in prototypes) {
if (key in gameobjects)
dainty_assign(gameobjects[key], prototypes[key]);
else {
/* Create this gameobject fresh */
Log.info("Making new prototype: " + key + " from " + prototypes[key].from);
var newproto = gameobjects[prototypes[key].from].clone(key);
gameobjects[key] = newproto;
for (var pkey in newproto)
if (typeof newproto[pkey] === 'object' && newproto[pkey] && 'clone' in newproto[pkey])
newproto[pkey] = newproto[pkey].clone();
dainty_assign(gameobjects[key], prototypes[key]);
}
}
}
prototypes.save_gameobjects = function() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); };
prototypes.from_file = function(file)
{
if (!IO.exists(file)) {
Log.error(`File ${file} does not exist.`);
return;
}
var newobj = gameobject.clone(file, {});
var script = IO.slurp(file);
newobj.$ = {};
var json = {};
if (IO.exists(file.name() + ".json")) {
json = JSON.parse(IO.slurp(file.name() + ".json"));
Object.assign(newobj.$, json.$);
delete json.$;
}
compile_env(`var self = this; var $ = self.$; ${script}`, newobj, file);
dainty_assign(newobj, json);
file = file.replaceAll('/', '.');
var path = file.name().split('.');
var nested_access = function(base, names) {
for (var i = 0; i < names.length; i++)
base = base[names[i]] = base[names[i]] || {};
return base;
};
var a = nested_access(ur, path);
a.tag = file.name();
prototypes.list.push(a.tag);
a.type = newobj;
a.instances = [];
newobj.ur = a;
return a;
}
prototypes.from_file.doc = "Create a new ur-type from a given script file.";
prototypes.list = [];
prototypes.from_obj = function(name, obj)
{
var newobj = gameobject.clone(name, obj);
prototypes.ur[name] = {
tag: name,
type: newobj
};
return prototypes.ur[name];
}
prototypes.load_config = function(name)
{
if (!prototypes.ur[name])
prototypes.ur[name] = gameobject.clone(name);
Log.warn(`Made new ur of name ${name}`);
return prototypes.ur[name];
}
prototypes.list_ur = function()
{
var list = [];
function list_obj(obj, prefix)
{
prefix ??= "";
var list = [];
for (var e in obj) {
list.push(prefix + e);
Log.warn("Descending into " + e);
list.concat(list_obj(obj[e], e + "."));
}
return list;
}
return list_obj(ur);
}
prototypes.get_ur = function(name)
{
if (!prototypes.ur[name]) {
if (IO.exists(name + ".js"))
prototypes.from_file(name + ".js");
prototypes.load_config(name);
return prototypes.ur[name];
} else
return prototypes.ur[name];
}
prototypes.from_obj("polygon2d", {
polygon2d: polygon2d.clone(),
});
prototypes.from_obj("edge2d", {
edge2d: bucket.clone(),
});
prototypes.from_obj("sprite", {
sprite: sprite.clone(),
});
prototypes.generate_ur = function(path)
{
var ob = IO.glob("*.js");
ob = ob.concat(IO.glob("**/*.js"));
ob = ob.filter(function(str) { return !str.startsWith("scripts"); });
ob.forEach(function(name) {
if (name === "game.js") return;
if (name === "play.js") return;
prototypes.from_file(name);
});
}
var ur = prototypes.ur;
prototypes.from_obj("camera2d", {
phys: gameobject.bodytype.kinematic,
speed: 300,
get zoom() { return this._zoom; },
set zoom(x) {
if (x <= 0) return;
this._zoom = x;
cmd(62, this._zoom);
},
_zoom: 1.0,
speedmult: 1.0,
selectable: false,
view2world(pos) {
return pos.mapc(mult, [1,-1]).add([-Window.width,Window.height].scale(0.5)).scale(this.zoom).add(this.pos);
},
world2view(pos) {
return pos.sub(this.pos).scale(1/this.zoom).add(Window.dimensions.scale(0.5));
},
});
prototypes.from_obj("arena", {});

View file

@ -108,6 +108,15 @@ var Player = {
this.pawns.forEach(x => x[fn]?.(...args));
},
mouse_input(type, ...args) {
for (var pawn of this.pawns.reverse()) {
if (typeof pawn.inputs?.mouse?.[type] === 'function') {
pawn.inputs.mouse[type].call(pawn,...args);
return;
}
}
},
raw_input(cmd, state, ...args) {
for (var pawn of this.pawns.reverse()) {
if (typeof pawn.inputs?.any === 'function') {

335
source/scripts/textedit.js Normal file
View file

@ -0,0 +1,335 @@
var texteditor = clone(inputpanel, {
title: "text editor",
_cursor:0, /* Text cursor: [char,line] */
get cursor() { return this._cursor; },
set cursor(x) {
if (x > this.value.length)
x = this.value.length;
if (x < 0)
x = 0;
this._cursor = x;
this.line = this.get_line();
},
submit() {},
line: 0,
killring: [],
undos: [],
startbuffer: "",
savestate() {
this.undos.push(this.value.slice());
},
popstate() {
if (this.undos.length === 0) return;
this.value = this.undos.pop();
this.cursor = this.cursor;
},
copy(start, end) {
return this.value.slice(start,end);
},
delete_line(p) {
var ls = this.line_start(p);
var le = this.line_end(p)+1;
this.cut_span(ls,le);
this.to_line_start();
},
line_blank(p) {
var ls = this.line_start(p);
var le = this.line_end(p);
var line = this.value.slice(ls, le);
if (line.search(/[^\s]/g) === -1)
return true;
else
return false;
},
get_line() {
var line = 0;
for (var i = 0; i < this.cursor; i++)
if (this.value[i] === "\n")
line++;
return line;
},
start() {
this.cursor = 0;
this.startbuffer = this.value.slice();
},
get dirty() {
return this.startbuffer !== this.value;
},
gui() {
GUI.text_cursor(this.value, [100,700],1,this.cursor+1);
GUI.text("C" + this.cursor + ":::L" + this.line + ":::" + (this.dirty ? "DIRTY" : "CLEAN"), [100,100], 1);
},
insert_char(char) {
this.value = this.value.slice(0,this.cursor) + char + this.value.slice(this.cursor);
this.cursor++;
},
input_enter_pressrep() {
var white = this.line_starting_whitespace(this.cursor);
this.insert_char('\n');
for (var i = 0; i < white; i++)
this.insert_char(" ");
},
input_text(char) {
if (Keys.ctrl() || Keys.alt()) return;
this.insert_char(char);
this.keycb();
},
input_backspace_pressrep() {
this.value = this.value.slice(0,this.cursor-1) + this.value.slice(this.cursor);
this.cursor--;
},
line_starting_whitespace(p) {
var white = 0;
var l = this.line_start(p);
while (this.value[l] === " ") {
white++;
l++;
}
return white;
},
cut_span(start, end) {
if (end < start) return;
this.savestate();
var ret = this.value.slice(start,end);
this.value = this.value.slice(0,start) + this.value.slice(end);
if (start > this.cursor)
return ret;
this.cursor -= ret.length;
return ret;
},
next_word(pos) {
var v = this.value.slice(pos+1).search(/[^\w]\w/g);
if (v === -1) return pos;
return pos + v + 2;
},
prev_word(pos) {
while (this.value.slice(pos,pos+2).search(/[^\w]\w/g) === -1 && pos > 0)
pos--;
return pos+1;
},
end_of_word(pos) {
var l = this.value.slice(pos).search(/\w[^\w]/g);
return l+pos;
},
get inset() {
return this.cursor - this.value.prev('\n', this.cursor) - 1;
},
line_start(p) {
return this.value.prev('\n', p)+1;
},
line_end(p) {
return this.value.next('\n', p);
},
next_line(p) {
return this.value.next('\n',p)+1;
},
prev_line(p) {
return this.line_start(this.value.prev('\n', p));
},
to_line_start() {
this.cursor = this.value.prev('\n', this.cursor)+1;
},
to_line_end() {
var p = this.value.next('\n', this.cursor);
if (p === -1)
this.to_file_end();
else
this.cursor = p;
},
line_width(pos) {
var start = this.line_start(pos);
var end = this.line_end(pos);
if (end === -1)
end = this.value.length;
return end-start;
},
to_file_end() { this.cursor = this.value.length; },
to_file_start() { this.cursor = 0; },
desired_inset: 0,
});
texteditor.inputs = {};
texteditor.inputs['C-s'] = function() {
editor.edit_level.script = texteditor.value;
editor.save_current();
texteditor.startbuffer = texteditor.value.slice();
};
texteditor.inputs['C-s'].doc = "Save script to file.";
texteditor.inputs['C-u'] = function() { this.popstate(); };
texteditor.inputs['C-u'].doc = "Undo.";
texteditor.inputs['C-q'] = function() {
var ws = this.prev_word(this.cursor);
var we = this.end_of_word(this.cursor)+1;
var find = this.copy(ws, we);
var obj = editor.edit_level.varname2obj(find);
if (obj) {
editor.unselect();
editor.selectlist.push(obj);
}
};
texteditor.inputs['C-q'].doc = "Select object of selected word.";
texteditor.inputs['C-o'] = function() {
this.insert_char('\n');
this.cursor--;
};
texteditor.inputs['C-o'].doc = "Insert newline.";
texteditor.inputs['C-o'].rep = true;
texteditor.inputs['M-o'] = function() {
while (this.line_blank(this.next_line(this.cursor)))
this.delete_line(this.next_line(this.cursor));
while (this.line_blank(this.prev_line(this.cursor)))
this.delete_line(this.prev_line(this.cursor));
};
texteditor.inputs['M-o'].doc = "Delete surround blank lines.";
texteditor.inputs['C-d'] = function () { this.value = this.value.slice(0,this.cursor) + this.value.slice(this.cursor+1); };
texteditor.inputs['C-d'].doc = "Delete character.";
texteditor.inputs['M-d'] = function() { this.cut_span(this.cursor, this.end_of_word(this.cursor)+1); };
texteditor.inputs['M-d'].doc = "Delete word.";
texteditor.inputs['C-a'] = function() {
this.to_line_start();
this.desired_inset = this.inset;
};
texteditor.inputs['C-a'].doc = "To start of line.";
texteditor.inputs['C-y'] = function() {
if (this.killring.length === 0) return;
this.insert_char(this.killring.pop());
};
texteditor.inputs['C-y'].doc = "Insert from killring.";
texteditor.inputs['C-e'] = function() {
this.to_line_end();
this.desired_inset = this.inset;
};
texteditor.inputs['C-e'].doc = "To line end.";
texteditor.inputs['C-k'] = function() {
if (this.cursor === this.value.length-1) return;
var killamt = this.value.next('\n', this.cursor) - this.cursor;
var killed = this.cut_span(this.cursor-1, this.cursor+killamt);
this.killring.push(killed);
};
texteditor.inputs['C-k'].doc = "Kill from cursor to end of line.";
texteditor.inputs['M-k'] = function() {
var prevn = this.value.prev('\n', this.cursor);
var killamt = this.cursor - prevn;
var killed = this.cut_span(prevn+1, prevn+killamt);
this.killring.push(killed);
this.to_line_start();
};
texteditor.inputs['M-k'].doc = "Kill entire line the cursor is on.";
texteditor.inputs['C-b'] = function() {
this.cursor--;
this.desired_inset = this.inset;
};
texteditor.inputs['C-b'].rep = true;
texteditor.inputs['M-b'] = function() {
this.cursor = this.prev_word(this.cursor-2);
this.desired_inset = this.inset;
};
texteditor.inputs['M-b'].rep = true;
texteditor.inputs['C-f'] = function() {
this.cursor++;
this.desired_inset = this.inset;
};
texteditor.inputs['C-f'].rep = true;
texteditor.inputs['M-f'] = function() {
this.cursor = this.next_word(this.cursor);
this.desired_inset = this.inset;
};
texteditor.inputs['M-f'].rep = true;
texteditor.inputs['C-p'] = function() {
if (this.cursor === 0) return;
this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.prev_line(this.cursor);
var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew);
};
texteditor.inputs['C-p'].rep = true;
texteditor.inputs['M-p'] = function() {
while (this.line_blank(this.cursor))
this.cursor = this.prev_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.prev_line(this.cursor);
};
texteditor.inputs['M-p'].doc = "Go up to next line with text on it.";
texteditor.inputs['M-p'].rep = true;
texteditor.inputs['C-n'] = function() {
if (this.cursor === this.value.length-1) return;
if (this.value.next('\n', this.cursor) === -1) {
this.to_file_end();
return;
}
this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.next_line(this.cursor);
var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew);
};
texteditor.inputs['C-n'].rep = true;
texteditor.inputs['M-n'] = function() {
while (this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
};
texteditor.inputs['M-n'].doc = "Go down to next line with text on it.";
texteditor.inputs['M-n'].rep = true;

View file

@ -5,7 +5,26 @@ out vec4 frag_color;
uniform sampler2D diffuse_texture;
float[] kernel = float[9](1.0/9.0,1.0/9.0,1.0/9.0,
1.0/9.0,1.0/9.0,1.0/9.0,
1.0/9.0,1.0/9.0,1.0/9.0);
void main()
{
frag_color = texture(diffuse_texture, TexCoords);
return;
vec2 res = vec2(640,360);
vec2 uv = gl_FragCoord.xy;
vec2 screen = textureSize(diffuse_texture,0);
vec3 acc = vec3(0);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
vec2 realRes = uv + vec2(i-1,j-1);
acc += texture(diffuse_texture, realRes / res).rgb * kernel[i*3+j];
}
}
// frag_color = vec4(acc,1);
}

View file

@ -12,11 +12,7 @@ uniform vec2 dimen;
void main()
{
// vec4 ipos = inverse(projection) * vec4(pos, 0.f, 1.f);
apos = pos * dimen;
// apos += offset;
// apos = pos + offset;
apos = ((pos*0.5)*dimen) + offset;
gl_Position = vec4(pos, 0.f, 1.f);
}