From cccd472f12c502c3d69c54adf68a9005ecbd6787 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 17 Nov 2023 21:16:13 +0000 Subject: [PATCH] Tween and delay fixes now entity lifetime; spline symmetry --- Makefile | 2 +- quickjs/Makefile | 18 ++++---- scripts/base.js | 13 ++++++ scripts/components.js | 20 +++++---- scripts/editor.js | 4 +- scripts/entity.js | 87 +++++++++++++++++++++++++++++++++++---- scripts/gui.js | 23 +++++++++-- scripts/sound.js | 11 ++++- scripts/std.js | 38 +++++++++++++++++ source/engine/jsffi.c | 22 +++++++++- source/engine/resources.c | 13 ++++++ source/engine/resources.h | 2 + source/engine/sound.c | 24 +++++------ 13 files changed, 231 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index 3195722..cd82006 100755 --- a/Makefile +++ b/Makefile @@ -198,7 +198,7 @@ input.md: $(INPUTMD) $(BIN)/libquickjs.a: make -C quickjs clean - make -C quickjs SYSRT=$(SYSRT) TTARGET=$(TTARGET) ARCH=$(ARCH) DBG=$(DBG) OPT=$(OPT) HOST_CC=$(CC) AR=$(AR) libquickjs.a libquickjs.lto.a CC=$(CC) + make -C quickjs SYSRT=$(SYSRT) TTARGET=$(TTARGET) ARCH=$(ARCH) DBG=$(DBG) OPT=$(OPT) HOST_CC=$(CC) CCC=$(CC) AR=$(AR) libquickjs.a libquickjs.lto.a CC=$(CC) @mkdir -p $(BIN) cp -rf quickjs/libquickjs.* $(BIN) diff --git a/quickjs/Makefile b/quickjs/Makefile index 5b190db..69d8095 100644 --- a/quickjs/Makefile +++ b/quickjs/Makefile @@ -22,9 +22,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -ifeq ($(shell uname -s),Darwin) -CONFIG_DARWIN=y -endif # Windows cross compilation from Linux #CONFIG_WIN32=y # use link time optimization (smaller and faster executables but slower build) @@ -34,12 +31,13 @@ CONFIG_LTO=y # force 32 bit build for some utilities #CONFIG_M32=y -ifdef CONFIG_DARWIN # use clang instead of gcc -CONFIG_CLANG=y -CONFIG_DEFAULT_AR=y +ifeq ($(CCC),clang) + CONFIG_CLANG=y + CONFIG_DEFAULT_AR=y endif + # installation directory prefix=/usr/local @@ -63,10 +61,9 @@ else CROSS_PREFIX= EXE= endif -ifdef CONFIG_CLANG - HOST_CC=clang +ifeq ($(CCC), clang) CC=$(CROSS_PREFIX)clang - CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d + CFLAGS += -g -Wall -MMD -MF $(OBJDIR)/$(@F).d CFLAGS += -Wextra CFLAGS += -Wno-sign-compare CFLAGS += -Wno-missing-field-initializers @@ -91,9 +88,8 @@ endif endif endif else - HOST_CC=gcc CC=$(CROSS_PREFIX)gcc - CFLAGS=-g -Wall -MMD -MF $(OBJDIR)/$(@F).d + CFLAGS += -g -Wall -MMD -MF $(OBJDIR)/$(@F).d CFLAGS += -Wno-array-bounds -Wno-format-truncation CFLAGS += -Wno-unused-function -Wno-unused-const-variable ifdef CONFIG_LTO diff --git a/scripts/base.js b/scripts/base.js index 50253ca..38ac74a 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -485,6 +485,19 @@ Object.defineProperty(String.prototype, 'dir', { value: function() { return this.tolast('/'); } }); +Object.defineProperty(String.prototype, 'splice', { + value: function(index, str, ) { + return this.slice(0,index) + str + this.slice(index); + } +}); + +Object.defineProperty(String.prototype, 'rm', { + value: function(index, endidx) { + endidx ??= index+1; + return this.slice(0,index) + this.slice(endidx); + } +}); + Object.defineProperty(String.prototype, 'updir', { value: function() { if (this.lastIndexOf('/') === this.length-1) diff --git a/scripts/components.js b/scripts/components.js index 3c4d41b..3f609f4 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -415,7 +415,8 @@ Geometry.doc = { doc: "Functions for creating a list of points for various geometric shapes.", box: "Create a box.", arc: "Create an arc, made of n points.", - circle: "Create a circle, made of n points." + circle: "Create a circle, made of n points.", + ngon: "Create a polygon of n sides.", }; /* For all colliders, "shape" is a pointer to a phys2d_shape, "id" is a pointer to the shape data */ @@ -532,12 +533,12 @@ 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() { +polygon2d.inputs['C-M-lm'] = function() { var idx = grab_from_points(Mouse.worldpos, this.points.map(p => this.gameobject.this2world(p)), 25); if (idx === -1) return; this.points.splice(idx, 1); }; -polygon2d.inputs['S-lm'].doc = "Remove point under mouse."; +polygon2d.inputs['C-M-lm'].doc = "Remove point under mouse."; polygon2d.inputs['C-b'] = function() { this.points = this.spoints; @@ -637,7 +638,7 @@ component.edge2d = Object.copy(collider2d, { return Spline.sample(degrees, this.dimensions, this.type, spoints, n); }, - samples: 10, + samples: 1, boundingbox() { return points2bb(this.points.map(x => x.scale(this.gameobject.scale))); @@ -678,7 +679,9 @@ component.edge2d = Object.copy(collider2d, { }, sample_calc() { - return (this.spoints().length-1); + var n = this.spoints().length-1; + if (this.looped) n++; + return n; }, samples_per_cp() { @@ -781,14 +784,17 @@ bucket.inputs['C-lm'] = function() { }; bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position."; -bucket.inputs['S-lm'] = function() { +bucket.inputs['C-M-lm'] = function() { var idx = grab_from_points(Mouse.worldpos, this.cpoints.map(p => this.gameobject.this2world(p)), 25); if (idx < 0 || idx > this.cpoints.length) return; this.cpoints.splice(idx, 1); }; -bucket.inputs['S-lm'].doc = "Remove point from the spline."; +bucket.inputs['C-M-lm'].doc = "Remove point from the spline."; + +bucket.inputs.lm = function(){}; +bucket.inputs.lm.released = function(){}; bucket.inputs.lb = function() { var np = []; diff --git a/scripts/editor.js b/scripts/editor.js index 05eeaa7..9c48abb 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -895,9 +895,7 @@ editor.inputs['C-s'] = function() { var savejs = saveobj.json_obj(); Object.merge(saveobj.__proto__, savejs); if (savejs.objects) saveobj.__proto__.objects = savejs.objects; - var path = saveobj.ur.toString(); - path = path.replaceAll('.','/'); - path = path + "/" + path.name() + ".json"; + var path = prototypes.ur_stem(saveobj.ur.toString()) + ".json"; IO.slurpwrite(JSON.stringify(saveobj.__proto__,null,1), path); Log.warn(`Wrote to file ${path}`); diff --git a/scripts/entity.js b/scripts/entity.js index d10ad77..5342175 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -42,11 +42,32 @@ var gameobject = { this.objects = {}; }, - timers:[], delay(fn, seconds) { - var t = timer.oneshot(fn.bind(this), seconds, this, false); + var t = timer.delay(fn.bind(this), seconds, false); this.timers.push(t); - return function() { t.kill(); }; + return t; + }, + + tween(prop, values, def){ + var t = Tween.make(this, prop, values, def); + t.play(); + + var k = function() { t.pause(); } + this.timers.push(k); + return k; + }, + + cry(file) { + Sound.play(file); + return; + + if (this.curcry && !Sound.finished(this.curcry)) return; + this.curcry = Sound.play(file); + var r = this.curcry; + Log.warn(r); + var fn = function() { Log.warn(r); if (r) Sound.stop(r); }; + this.timers.push(fn); + return fn; }, set max_velocity(x) { cmd(151, this.body, x); }, @@ -451,10 +472,7 @@ var gameobject = { q_body(8,this.body); Game.unregister_obj(this); - this.timers.forEach(function(t) { - if (!t) return; - t.kill(); - }); + this.timers.forEach(t => t()); if (this.level) { this.level.remove_obj(this); @@ -503,6 +521,7 @@ var gameobject = { obj.body = make_gameobject(); obj.components = {}; obj.objects = {}; + obj.timers = []; assign_impl(obj, gameobject.impl); obj._ed = { selectable: true, @@ -656,6 +675,7 @@ gameobject.doc = { kill: `Remove this object from the world.`, level: "The entity this entity belongs to.", delay: 'Run the given function after the given number of seconds has elapsed.', + cry: 'Make a sound. Can only make one at a time.', }; /* Default objects */ @@ -895,3 +915,56 @@ prototypes.resani = function(ur, path) } return restry; } + +prototypes.ur_dir = function(ur) +{ + var path = ur.replaceAll('.', '/'); + Log.warn(path); + Log.warn(IO.exists(path)); + Log.warn(`${path} does not exist; sending ${path.dir()}`); +} + +prototypes.ur_json = function(ur) +{ + var path = ur.replaceAll('.', '/'); + if (IO.exists(path)) + path = path + "/" + path.name() + ".json"; + else + path = path + ".json"; + + return path; +} + +prototypes.ur_stem = function(ur) +{ + var path = ur.replaceAll('.', '/'); + if (IO.exists(path)) + return path + "/" + path.name(); + else + return path; +} + +prototypes.ur_file_exts = ['.jso', '.json']; + +prototypes.ur_folder = function(ur) +{ + var path = ur.replaceAll('.', '/'); + return IO.exists(path); +} + +prototypes.ur_pullout_folder = function(ur) +{ + if (!prototypes.ur_folder(ur)) return; + + var stem = prototypes.ur_stem(ur); + +/* prototypes.ur_file_exts.forEach(function(e) { + var p = stem + e; + if (IO.exists(p)) + */ +} + +prototypes.ur_pushin_folder = function(ur) +{ + +} diff --git a/scripts/gui.js b/scripts/gui.js index d24bcfc..fe3d285 100644 --- a/scripts/gui.js +++ b/scripts/gui.js @@ -391,10 +391,19 @@ Ease.elastic.c5 = 2*Math.PI / 4.5; var Tween = { default: { - loop: "restart", /* none, restart, yoyo, circle */ + loop: "restart", + /* + loop types + none: when done, return to first value + hold: hold last value of tween + restart: restart at beginning, looping + yoyo: go up and then back down + circle: go up and back down, looped + */ time: 1, /* seconds to do */ ease: Ease.linear, - whole: true, + whole: true, /* True if time is for the entire tween, false if each stage */ + cb: function(){}, }, start(obj, target, tvals, options) @@ -416,6 +425,13 @@ var Tween = { defn.fn = function(dt) { defn.accum += dt; + if (defn.accum >= defn.time && defn.loop === 'hold') { + obj[target] = tvals[tvals.length-1]; + defn.pause(); + defn.cb.call(obj); + return; + } + defn.pct = (defn.accum % defn.time) / defn.time; if (defn.loop === 'none' && defn.accum >= defn.time) defn.stop(); @@ -445,8 +461,9 @@ var Tween = { }; defn.stop = function() { if (!playing) return; defn.pause(); defn.restart(); }; defn.pause = function() { + Register.update.unregister(defn.fn); if (!playing) return; - Register.update.unregister(defn.fn); + playing = false; }; diff --git a/scripts/sound.js b/scripts/sound.js index 9464637..46ee5c9 100644 --- a/scripts/sound.js +++ b/scripts/sound.js @@ -27,7 +27,16 @@ var Sound = { Log.error(`Cannot play sound ${file}: does not exist.`); return; } - this.id = cmd(14,file); + var p = cmd(14,file); + return p; + }, + + finished(sound) { + return cmd(165, sound); + }, + + stop(sound) { + cmd(164, sound); }, music(midi, sf) { diff --git a/scripts/std.js b/scripts/std.js index 159c1cf..e57ab81 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -101,6 +101,15 @@ Log.doc = { clear: "Clear console." }; +/* + IO path rules. Starts with, meaning: + "@": playerpath + "/": game room + "#": Force look locally (instead of in db first) + - This is combined with others. #/, #@, etc + "": Local path relative to script defined in +*/ + var IO = { exists(file) { return cmd(65, file);}, slurp(file) { @@ -116,6 +125,16 @@ var IO = { return paths; }, ls() { return cmd(66); }, + /* Only works on text files currently */ + cp(f1, f2) { + cmd(166, f1, f2); + }, + mv(f1, f2) { + return cmd(163, f1, f2); + }, + rm(f) { + return cmd(f); + }, glob(pat) { var paths = IO.ls(); pat = pat.replaceAll(/([\[\]\(\)\^\$\.\|\+])/g, "\\$1"); @@ -137,6 +156,25 @@ IO.doc = { glob: "Glob files in game directory.", }; +var Parser = {}; +Parser.replstrs = function(path) +{ + var script = IO.slurp(path); + var regexp = /"[^"\s]*?\.[^"\s]+?"/g; + var stem = path.dir(); + + script = script.replace(regexp,function(str) { + if (str[1] === "/") + return str.rm(1); + + if (str[1] === "@") + return str.rm(1).splice(1, "playerpath/"); + + return str.splice(1, stem + "/"); + }); + Log.warn(script); +} + var Cmdline = {}; Cmdline.cmds = []; diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 3845e2d..2065cdc 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -546,7 +546,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) case 14: str = JS_ToCString(js, argv[1]); - play_oneshot(make_sound(str)); + ret = ptr2js(play_sound(make_sound(str))); break; case 15: @@ -1152,6 +1152,26 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) case 161: ret = vec2js(mat_t_dir(t_go2world(js2go(argv[1])), js2vec2(argv[2]))); break; + case 162: + str = JS_ToCString(js, argv[1]); + ret = int2js(remove(str)); + break; + case 163: + str = JS_ToCString(js,argv[1]); + str2 = JS_ToCString(js,argv[2]); + ret = int2js(rename(str, str2)); + break; + case 164: + sound_stop(js2ptr(argv[1])); + break; + case 165: + ret = bool2js(sound_paused(js2ptr(argv[1]))); + break; + case 166: + str = js2str(argv[1]); + str2 = js2str(argv[2]); + ret = int2js(cp(str, str2)); + break; } if (str) diff --git a/source/engine/resources.c b/source/engine/resources.c index 47b496b..6a68257 100644 --- a/source/engine/resources.c +++ b/source/engine/resources.c @@ -247,6 +247,19 @@ char *slurp_text(const char *filename, size_t *size) return retstr; } +int cp(char *p1, char *p2) +{ + long len; + void *data = slurp_file(p1, &len); + + FILE *f = fopen_mkdir(p2, "w"); + if (!f) return 1; + fwrite(data, len, 1, f); + free(data); + fclose(f); + return 0; +} + void rek_mkdir(char *path) { char *sep = strrchr(path, '/'); if(sep != NULL) { diff --git a/source/engine/resources.h b/source/engine/resources.h index e1ea8be..063df4e 100644 --- a/source/engine/resources.h +++ b/source/engine/resources.h @@ -14,7 +14,9 @@ FILE *res_open(char *path, const char *tag); FILE *path_open(const char *tag, const char *fmt, ...); char *make_path(const char *file); char **ls(char *path); +int cp(char *p1, char *p2); int fexists(char *path); +FILE *fopen_mkdir(char *path, char *mode); void *slurp_file(const char *filename, size_t *size); char *slurp_text(const char *filename, size_t *size); diff --git a/source/engine/sound.c b/source/engine/sound.c index 44d51af..6f77677 100644 --- a/source/engine/sound.c +++ b/source/engine/sound.c @@ -233,42 +233,43 @@ void play_oneshot(struct wav *wav) { struct sound *play_sound(struct wav *wav) { struct sound *new = calloc(1, sizeof(*new)); new->data = wav; - new->bus = first_free_bus(dsp_filter(new, sound_fillbuf)); new->playing = 1; - + new->loop = 0; + new->frame = 0; + new->endcb = kill_oneshot; return new; } int sound_playing(const struct sound *s) { - return s->playing; + return !sound_paused(s); } int sound_paused(const struct sound *s) { - return (!s->playing && s->frame < s->data->frames); + return s->bus == NULL; } void sound_pause(struct sound *s) { - s->playing = 0; + if (s->bus == NULL) return; bus_free(s->bus); + s->bus = NULL; } void sound_resume(struct sound *s) { - s->playing = 1; + if (s->bus != NULL) return; s->bus = first_free_bus(dsp_filter(s, sound_fillbuf)); } void sound_stop(struct sound *s) { - s->playing = 0; + sound_pause(s); s->frame = 0; - bus_free(s->bus); } int sound_finished(const struct sound *s) { - return !s->playing && s->frame == s->data->frames; + return s->frame == s->data->frames; } int sound_stopped(const struct sound *s) { - return !s->playing && s->frame == 0; + return s->bus == NULL; } struct mp3 make_music(const char *mp3) { @@ -298,8 +299,7 @@ void sound_fillbuf(struct sound *s, soundbyte *buf, int n) { s->frame++; if (s->frame == s->data->frames) { - bus_free(s->bus); - s->bus = NULL; + sound_stop(s); s->endcb(s); return; }