From 9701cd2b4ab4db8374e7e66e7438747945c3489a Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 6 Oct 2023 17:38:49 +0000 Subject: [PATCH] Mum padding and text wrap; save as works --- scripts/components.js | 2 + scripts/editor.js | 83 +++++++++++++++++++++++++----------------- scripts/engine.js | 4 +- scripts/entity.js | 26 ++++++++++--- scripts/gui.js | 45 +++++++++++++++++------ source/engine/font.c | 7 ++-- source/engine/sound.c | 4 ++ source/engine/yugine.c | 2 +- 8 files changed, 117 insertions(+), 56 deletions(-) diff --git a/scripts/components.js b/scripts/components.js index 70bc30d..2d37882 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -428,6 +428,8 @@ polygon2d.inputs['C-lm'] = function() { this.points.push(this.gameobject.world2this(Mouse.worldpos)); }; polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse."; +polygon2d.inputs.lm = function(){}; +polygon2d.inputs.lm.released = function(){}; polygon2d.inputs['S-lm'] = function() { var idx = grab_from_points(Mouse.worldpos, this.points.map(p => this.gameobject.this2world(p)), 25); diff --git a/scripts/editor.js b/scripts/editor.js index 8360de6..62c9075 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -40,7 +40,7 @@ var editor = { /* Tries to select id */ do_select(go) { var obj = go >= 0 ? Game.object(go) : undefined; - if (!obj || !obj.selectable) return undefined; + if (!obj || !obj._ed.selectable) return undefined; if (obj.level !== this.edit_level) { var testlevel = obj.level; @@ -351,7 +351,7 @@ var editor = { this.edit_level = Primum.spawn(ur.arena); // this.edit_level.toString = function() { return "desktop"; }; - editor.edit_level.selectable = false; + editor.edit_level._ed.selectable = false; }, _sel_comp: undefined, @@ -467,13 +467,13 @@ var editor = { } editor.edit_level.objects.forEach(function(obj) { - if (!obj.selectable) + if (!obj._ed.selectable) GUI.image("icons/icons8-lock-16.png", world2screen(obj.worldpos())); }); Debug.draw_grid(1, editor_config.grid_size, Color.Editor.grid.alpha(0.3)); - var startgrid = screen2world([-20,Window.height]).map(function(x) { return Math.snap(x, editor_config.grid_size); }); - var endgrid = screen2world([Window.width, 0]); + var startgrid = screen2world([-20,0]).map(function(x) { return Math.snap(x, editor_config.grid_size); }); + var endgrid = screen2world([Window.width, Window.height]); var w_step = Math.round(editor_config.ruler_mark_px/Window.width * (endgrid.x-startgrid.x)/editor_config.grid_size)*editor_config.grid_size; if (w_step === 0) w_step = editor_config.grid_size; @@ -539,6 +539,7 @@ var editor = { lvl_history: [], load(file) { + Log.warn("LOADING " + file); var ur = prototypes.get_ur(file); if (!ur) return; var obj = editor.edit_level.spawn(ur); @@ -583,15 +584,24 @@ var editor = { if (curur) { notifypanel.action = editor.saveas; - this.openpanel(gen_notify("Entity already exists with that name. Overwrite?", this.saveas.bind(this, sub))); + this.openpanel(gen_notify("Entity already exists with that name. Delete first.")); } else { var path = sub.replaceAll('.', '/') + ".json"; var saveobj = obj.level_obj(); IO.slurpwrite(JSON.stringify(saveobj,null,1), path); - var t = obj.transform(); + + + if (obj === editor.edit_level) { + obj.clear(); + var nobj = editor.edit_level.spawn(sub); + editor.selectlist = [nobj]; + return; + } + + var t = obj.transform(); obj.kill(); editor.unselect(); - editor.load(sub); + obj = editor.load(sub); obj.pos = t.pos; obj.angle = t.angle; } @@ -747,6 +757,10 @@ editor.inputs['M-p'] = function() { editor.inputs['M-p'].doc = "Do one time step, pausing if necessary."; editor.inputs['C-M-p'] = function() { + if (!Game.playing()) { + editor.start_play_ed(); + Game.editor_mode(false); + } Log.warn(`Starting edited level ...`); }; editor.inputs['C-M-p'].doc = "Start game from currently edited level."; @@ -811,10 +825,10 @@ 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.selectable = false; }); }; +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.selectable = true; }); }; +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."; editor.inputs['C-n'] = function() { @@ -1261,19 +1275,28 @@ var inputpanel = { pos:[100,Window.height-50], wh:[350,600], anchor: [0,1], + padding:[5,-15], gui() { - var win = Mum.window({width:this.wh.x,height:this.wh.y, color:Color.black.alpha(0.1), anchor:this.anchor}); + this.win = Mum.window({width:this.wh.x,height:this.wh.y, color:Color.black.alpha(0.1), anchor:this.anchor, padding:this.padding}); var itms = this.guibody(); if (!Array.isArray(itms)) itms = [itms]; - win.items = itms; - win.draw(this.pos.slice()); + if (this.title) + this.win.items = [ + Mum.column({items: [ + Mum.text({str:this.title}), + ...itms + ]}) + ]; + else + this.win.items = itms; + this.win.draw(this.pos.slice()); }, guibody() { return [ Mum.text({str:this.value, color:Color.green}), -// Mum.button({str:"Submit", action:this.submit}) + Mum.button({str:"SUBMIT", action:this.submit.bind(this)}) ]; }, @@ -1339,7 +1362,7 @@ inputpanel.inputs.char = function(c) { this.keycb(); } inputpanel.inputs['C-d'] = function() { this.value = this.value.slice(0,this.caret) + this.value.slice(this.caret+1); }; -inputpanel.inputs.tab = function() { this.value = tab_complete(this.value, this.assets); } +inputpanel.inputs.tab = function() { this.value = tab_complete(this.value, this.assets); this.caret = this.value.length;} inputpanel.inputs.escape = function() { this.close(); } inputpanel.inputs['C-b'] = function() { if (this.caret === 0) return; @@ -1411,11 +1434,12 @@ function proto_children(name) { load("scripts/textedit.js"); var replpanel = Object.copy(inputpanel, { - title: "REPL", + title: "", closeonsubmit:false, wh: [700,300], pos: [50,50], anchor: [0,0], + padding: [0,0], guibody() { var log = cmd(84); @@ -1649,6 +1673,7 @@ var openlevelpanel = Object.copy(inputpanel, { start() { this.allassets = prototypes.list.sort(); this.assets = this.allassets.slice(); + this.caret = 0; var click_ur = function(btn) { Log.warn(btn.str); this.value = btn.str; @@ -1659,7 +1684,7 @@ var openlevelpanel = Object.copy(inputpanel, { this.mumlist = []; this.assets.forEach(function(x) { - this.mumlist[x] = Mum.text({str:x, action:click_ur,selectable: true, hovered:{color:Color.red}}); + this.mumlist[x] = Mum.button({str:x, action:click_ur}); }, this); }, @@ -1680,7 +1705,8 @@ var openlevelpanel = Object.copy(inputpanel, { }); var saveaspanel = Object.copy(inputpanel, { - title: "save level as", + get title() { return `save level as: ${this.stem}.`; }, + action() { var savename = ""; if (this.stem) savename += this.stem + "."; @@ -1712,17 +1738,10 @@ var notifypanel = Object.copy(inputpanel, { }, guibody() { - Nuke.label(this.msg); - Nuke.newline(2); - if (Nuke.button("OK")) { - if ('yes' in this) - this.yes(); - this.close(); - } - }, - - input_n_pressed() { - this.close(); + return Mum.column({items: [ + Mum.text({str:this.msg}), + Mum.button({str:"OK", action:this.close.bind(this)}) + ]}); }, }); @@ -1817,7 +1836,7 @@ var entitylistpanel = Object.copy(inputpanel, { } x.visible = Nuke.checkbox(x.visible); - x.selectable = Nuke.checkbox(x.selectable); + x._ed.selectable = Nuke.checkbox(x._ed.selectable); if (editor.selectlist.includes(x)) Nuke.label("T"); else Nuke.label("F"); }); @@ -1845,14 +1864,12 @@ limited_editor.inputs['M-p'] = function() limited_editor.inputs['C-q'] = function() { Game.stop(); - game.stop(); Sound.killall(); Player.players[0].uncontrol(limited_editor); Player.players[0].control(editor); Register.gui.register(editor.ed_gui, editor); Debug.register_call(editor.ed_debug, editor); -// World.kill(); - World.clear_all(); + Primum.clear_all(); editor.load_json(editor.stash); Game.view_camera(editor.camera); } diff --git a/scripts/engine.js b/scripts/engine.js index 7bd5a2e..c082635 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -693,13 +693,15 @@ var preprimum = {}; preprimum.objects = {}; preprimum.worldpos = function() { return [0,0]; }; preprimum.worldangle = function() { return 0; }; +preprimum.scale = 1; +preprimum.gscale = function() { return 1; }; preprimum.pos = [0,0]; preprimum.angle = 0; var World = gameobject.make(preprimum); var Primum = World; Primum.level = undefined; Primum.toString = function() { return "Primum"; }; -Primum.selectable = false; +Primum._ed.selectable = false; World.reparent = function(parent) { Log.warn("Cannot reparent the Primum."); } /* Load configs */ diff --git a/scripts/entity.js b/scripts/entity.js index 889b149..7bad63d 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -12,8 +12,20 @@ function grab_from_points(pos, points, slop) { var gameobject = { impl: { - get scale() { return cmd(103, this.body); }, + clear() { + this.objects.forEach(function(x) { + x.kill(); + }); + this.objects = {}; + }, + gscale() { return cmd(103,this.body); }, + get scale() { + if (!this.level) return this.gscale(); + return this.gscale()/this.level.gscale(); + }, set scale(x) { + if (this.level) + x *= this.level.gscale(); var pct = x/this.scale; cmd(36, this.body, x); @@ -260,6 +272,7 @@ var gameobject = { json_obj() { var d = ediff(this,this.__proto__); + if (!d) return {}; delete d.pos; delete d.angle; delete d.velocity; @@ -381,7 +394,10 @@ var gameobject = { Object.mixin(obj, gameobject.impl); Object.hide(obj, 'components'); Object.hide(obj, 'objects'); - obj._ed = {}; + obj._ed = { + selectable: true, + dirty: false, + }; Object.hide(obj, '_ed'); obj.ur = this.toString(); Object.hide(obj,'ur'); @@ -428,10 +444,8 @@ var gameobject = { Log.warn(`No object with name ${name}. Could not rename to ${newname}.`); return; } - if (this.objects[newname]) { - Log.warn(`Already an object with name ${newname}.`); + if (this.objects[newname]) return; - } this.objects[newname] = this.objects[name]; delete this.objects[name]; @@ -556,6 +570,7 @@ prototypes.from_obj = function(name, obj) { var newur = Object.copy(gameobject, obj); prototypes.ur[name] = newur; + prototypes.list.push(name); newur.toString = function() { return name; }; return prototypes.ur[name]; } @@ -695,4 +710,3 @@ prototypes.resani = function(ur, path) } return restry; } - diff --git a/scripts/gui.js b/scripts/gui.js index 1175e73..6f006da 100644 --- a/scripts/gui.js +++ b/scripts/gui.js @@ -114,9 +114,11 @@ var Mum = { }, text_outline: 1, /* outline in pixels */ color: Color.white, - margin: [5,5], /* Distance between elements for things like columns */ + margin: [0,0], /* Distance between elements for things like columns */ width: 0, height: 0, + max_width: Infinity, + max_height: Infinity, image_repeat: false, image_repeat_offset: [0,0], debug: false, /* set to true to draw debug boxes */ @@ -136,17 +138,20 @@ var Mum = { extend(def) { var n = Object.create(this); Object.assign(n, def); - return function(def) { + var fn = function(def) { var p = n.make(def); p.prestart(); p.start(); return p; }; + fn._int = n; + return fn; }, } Mum.text = Mum.extend({ - draw(cursor) { + draw(cursor, cnt) { + cnt ??= Mum; if (this.hide) return; if (this.selectable) gui_controls.options.push_unique(this); this.caret ??= -1; @@ -158,10 +163,13 @@ Mum.text = Mum.extend({ */ var params = this.selected ? this.hovered : this; + this.width = Math.min(params.max_width, cnt.max_width); + this.calc_bb(cursor); + this.height = this.wh.y; var aa = [0,1].sub(params.anchor); var pos = cursor.add(params.wh.scale(aa)).add(params.offset); - ui_text(params.str, pos, params.font_size, params.color, params.width, params.caret); + ui_text(params.str, pos, params.font_size, params.color, this.width, params.caret); }, update_bb(cursor) { @@ -179,22 +187,34 @@ Mum.text = Mum.extend({ }, }); +Mum.button = Mum.text._int.extend({ + selectable: true, + color: Color.blue, + hovered:{ + color: Color.red + }, + action() { Log.warn("Button has no action."); }, +}); + Mum.window = Mum.extend({ start() { this.wh = [this.width, this.height]; this.bb = cwh2bb([0,0], this.wh); }, - draw(pos) { - var p = pos.sub(this.wh.scale(this.anchor)); + draw(cursor, cnt) { + cnt ??= Mum; + var p = cursor.sub(this.wh.scale(this.anchor)).add(this.padding); GUI.window(p,this.wh, this.color); this.bb = bl2bb(p, this.wh); - var pos = [this.bb.l, this.bb.t]; GUI.flush(); GUI.scissor(p.x,p.y,this.wh.x,this.wh.y); + this.max_width = this.width; + + var pos = [this.bb.l, this.bb.t].add(this.padding); this.items.forEach(function(item) { if (item.hide) return; - item.draw(pos.slice()); - }); + item.draw(pos.slice(),this); + }, this); GUI.flush(); GUI.scissor_win(); }, @@ -229,14 +249,15 @@ Mum.image = Mum.extend({ }); Mum.column = Mum.extend({ - draw(cursor) { + draw(cursor, cnt) { if (this.hide) return; cursor = cursor.add(this.offset); + this.max_width = cnt.width; this.items.forEach(function(item) { if (item.hide) return; - item.draw(cursor); - cursor.y -= item.wh.y; + item.draw(cursor, this); + cursor.y -= item.height; cursor.y -= this.padding.y; }, this); }, diff --git a/source/engine/font.c b/source/engine/font.c index be96485..5e2922c 100644 --- a/source/engine/font.c +++ b/source/engine/font.c @@ -79,8 +79,9 @@ void font_init() { .label = "text buffer" }); -// font = MakeFont("fonts/LessPerfectDOSVGA.ttf", 16); - font = MakeFont("fonts/c64.ttf", 8); + font = MakeFont("fonts/LessPerfectDOSVGA.ttf", 16); +// font = MakeFont("fonts/c64.ttf", 8); +// font = MakeFont("fonts/teenytinypixels.ttf", 16); bind_text.fs.images[0] = font->texID; bind_text.fs.samplers[0] = sg_make_sampler(&(sg_sampler_desc){}); } @@ -138,7 +139,7 @@ struct sFont *MakeFont(const char *fontfile, int height) { stbtt_GetFontVMetrics(&fontinfo, &newfont->ascent, &newfont->descent, &newfont->linegap); newfont->emscale = stbtt_ScaleForMappingEmToPixels(&fontinfo, 16); - newfont->linegap = (newfont->ascent - newfont->descent) * newfont->emscale; + newfont->linegap = (newfont->ascent - newfont->descent) * 1.5*newfont->emscale/2; newfont->texID = sg_make_image(&(sg_image_desc){ .type = SG_IMAGETYPE_2D, diff --git a/source/engine/sound.c b/source/engine/sound.c index eeacdfe..c7acf90 100644 --- a/source/engine/sound.c +++ b/source/engine/sound.c @@ -151,6 +151,10 @@ struct wav *make_sound(const char *wav) { struct wav mwav; long rawlen; void *raw = slurp_file(wav, &rawlen); + if (!raw) { + YughError("Could not find file %s.", wav); + return; + } if (!strcmp(ext, "wav")) mwav.data = drwav_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav.ch, &mwav.samplerate, &mwav.frames, NULL); diff --git a/source/engine/yugine.c b/source/engine/yugine.c index 61b99ff..725ef0a 100644 --- a/source/engine/yugine.c +++ b/source/engine/yugine.c @@ -112,7 +112,7 @@ void seghandle(int sig) { if (strsignal(sig)) YughCritical("CRASH! Signal: %s.", strsignal(sig)); -// print_stacktrace(); + js_stacktrace(); exit(1); #endif