diff --git a/source/engine/3d/model.c b/source/engine/3d/model.c index c5eff0b..75abcaf 100644 --- a/source/engine/3d/model.c +++ b/source/engine/3d/model.c @@ -35,14 +35,14 @@ struct model *MakeModel(const char *path) { if (!result == cgltf_result_success) { YughError("Could not read file %s.", path); - return; + return NULL; } result = cgltf_load_buffers(&options, data, path); if (!result == cgltf_result_success) { YughError("Could not load buffers for file %s.", path); - return; + return NULL; } struct model *model = malloc(sizeof(struct model)); diff --git a/source/engine/debug/log.c b/source/engine/debug/log.c index 29659b7..4a2dd68 100644 --- a/source/engine/debug/log.c +++ b/source/engine/debug/log.c @@ -30,6 +30,7 @@ char consolelog[CONSOLE_BUF] = {'\0'}; void mYughLog(int category, int priority, int line, const char *file, const char *message, ...) { + if (priority >= logLevel) { time_t now = time(0); char *dt = ctime(&now); @@ -45,6 +46,9 @@ void mYughLog(int category, int priority, int line, const char *file, const char snprintf(buffer, ERROR_BUFFER, "%s:%d: %s, %s: %s\n", file, line, logstr[priority], catstr[category], msgbuffer); log_print(buffer); + + if (priority >= 2) + js_stacktrace(); } } diff --git a/source/engine/ffi.c b/source/engine/ffi.c index c86ff84..5e82c66 100644 --- a/source/engine/ffi.c +++ b/source/engine/ffi.c @@ -988,6 +988,10 @@ JSValue duk_register(JSContext *js, JSValueConst this, int argc, JSValueConst *a case 8: register_gamepad(c); break; + + case 9: + stacktrace_callee = c; + break; } return JS_NULL; @@ -1455,8 +1459,8 @@ JSValue duk_make_timer(JSContext *js, JSValueConst this, int argc, JSValueConst { double secs = js2number(argv[1]); struct callee *c = malloc(sizeof(*c)); - c->fn = argv[0]; - c->obj = argv[2]; + c->fn = JS_DupValue(js,argv[0]); + c->obj = JS_GetGlobalObject(js); int id = timer_make(secs, call_callee, c, 1); return JS_NewInt64(js, id); diff --git a/source/engine/input.c b/source/engine/input.c index 8be7a0f..f120101 100644 --- a/source/engine/input.c +++ b/source/engine/input.c @@ -6,6 +6,7 @@ #include "log.h" #include "ffi.h" #include "time.h" +#include "font.h" int32_t mouseWheelX = 0; int32_t mouseWheelY = 0; @@ -27,6 +28,27 @@ static int mquit = 0; static struct callee pawn_callee; static struct callee gamepad_callee; +const char *gamepad2str(int btn) +{ + switch(btn) { + case GLFW_GAMEPAD_BUTTON_CROSS: return "cross"; + case GLFW_GAMEPAD_BUTTON_CIRCLE: return "circle"; + case GLFW_GAMEPAD_BUTTON_SQUARE: return "square"; + case GLFW_GAMEPAD_BUTTON_TRIANGLE: return "triangle"; + case GLFW_GAMEPAD_BUTTON_START: return "start"; + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: return "lbump"; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: return "rbump"; + case GLFW_GAMEPAD_BUTTON_GUIDE: return "guide"; + case GLFW_GAMEPAD_BUTTON_BACK: return "back"; + case GLFW_GAMEPAD_BUTTON_DPAD_UP: return "dup"; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: return "ddown"; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: return "dleft"; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: return "dright"; + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: return "lthumb"; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: return "rthumb"; + } +} + void register_pawn(struct callee c) { pawn_callee = c; @@ -163,6 +185,9 @@ void joystick_cb(int jid, int event) } } +JSValue jsgamepadstr[15]; +JSValue jsaxesstr[4]; + void input_init() { glfwSetCursorPosCallback(mainwin->window, cursor_pos_cb); @@ -175,6 +200,14 @@ void input_init() glfwUpdateGamepadMappings(paddb); free(paddb); + for (int b = 0; b < 15; b++) + jsgamepadstr[b] = str2js(gamepad2str(b)); + + jsaxesstr[0] = str2js("axis_ljoy"); + jsaxesstr[1] = str2js("axis_rjoy"); + jsaxesstr[2] = str2js("axis_ltrigger"); + jsaxesstr[3] = str2js("axis_rtrigger"); + /* Grab all joysticks initially present */ for (int i = 0; i < 16; i++) if (glfwJoystickPresent(i)) joystick_add(i); @@ -327,26 +360,6 @@ void call_input_down(int *key) { call_input_signal(keystr); } -const char *gamepad2str(int btn) -{ - switch(btn) { - case GLFW_GAMEPAD_BUTTON_CROSS: return "cross"; - case GLFW_GAMEPAD_BUTTON_CIRCLE: return "circle"; - case GLFW_GAMEPAD_BUTTON_SQUARE: return "square"; - case GLFW_GAMEPAD_BUTTON_TRIANGLE: return "triangle"; - case GLFW_GAMEPAD_BUTTON_START: return "start"; - case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: return "lbump"; - case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: return "rbump"; - case GLFW_GAMEPAD_BUTTON_GUIDE: return "guide"; - case GLFW_GAMEPAD_BUTTON_BACK: return "back"; - case GLFW_GAMEPAD_BUTTON_DPAD_UP: return "dup"; - case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: return "ddown"; - case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: return "dleft"; - case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: return "dright"; - case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: return "lthumb"; - case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: return "rthumb"; - } -} const char *axis2str(int axis) { @@ -380,46 +393,50 @@ void input_poll(double wait) if (!glfwGetGamepadState(joysticks[i].id, &state)) continue; JSValue argv[3]; - argv[0] = int2js(joysticks[i].id); + argv[0] = num_cache[joysticks[i].id]; char inputstr[50]; for (int b = 0; b < 15; b++) { - argv[1] = str2js(gamepad2str(b)); + argv[1] = jsgamepadstr[b]; if (state.buttons[b]) { - argv[2] = int2js(0); + argv[2] = num_cache[0]; script_callee(gamepad_callee,3,argv); if (!joysticks[i].state.buttons[b]) { - argv[2] = int2js(1); + argv[2] = num_cache[1]; script_callee(gamepad_callee,3,argv); } } else if (!state.buttons[b] && joysticks[i].state.buttons[b]) { - argv[2] = int2js(2); + argv[2] = num_cache[2]; script_callee(gamepad_callee,3,argv); } } - argv[1] = str2js("axis_ljoy"); + argv[1] = jsaxesstr[0]; cpVect v; v.x = state.axes[0]; v.y = -state.axes[1]; argv[2] = vec2js(v); script_callee(gamepad_callee,3,argv); + JS_FreeValue(js, argv[2]); - argv[1] = str2js("axis_rjoy"); + argv[1] = jsaxesstr[1]; v.x = state.axes[2]; v.y = -state.axes[3]; argv[2] = vec2js(v); script_callee(gamepad_callee,3,argv); + JS_FreeValue(js, argv[2]); - argv[1] = str2js("axis_ltrigger"); + argv[1] = jsaxesstr[2]; argv[2] = num2js((state.axes[4]+1)/2); script_callee(gamepad_callee,3,argv); + JS_FreeValue(js, argv[2]); - argv[1] = str2js("axis_rtrigger"); + argv[1] = jsaxesstr[3]; argv[2] = num2js((state.axes[5]+1)/2); - script_callee(gamepad_callee,3,argv); + script_callee(gamepad_callee,3,argv); + JS_FreeValue(js, argv[2]); joysticks[i].state = state; } diff --git a/source/engine/script.c b/source/engine/script.c index b1a148d..07bcac4 100644 --- a/source/engine/script.c +++ b/source/engine/script.c @@ -30,20 +30,27 @@ static int load_prefab(const char *fpath, const struct stat *sb, int typeflag) { void script_startup() { rt = JS_NewRuntime(); + JS_SetMaxStackSize(rt,0); js = JS_NewContext(rt); ffi_load(); } +JSValue num_cache[100] = {0}; + void script_init() { /* Load all prefabs into memory */ script_dofile("scripts/engine.js"); - script_dofile("config.js"); + + for (int i = 0; i < 100; i++) + num_cache[i] = int2js(i); } void script_run(const char *script) { JS_FreeValue(js, JS_Eval(js, script, strlen(script), "script", 0)); } +struct callee stacktrace_callee; + time_t file_mod_secs(const char *file) { struct stat attr; stat(file, &attr); @@ -52,23 +59,15 @@ time_t file_mod_secs(const char *file) { void js_stacktrace() { - YughWarn("Dumping stack ..."); - JSValue error = JS_NewError(js); - JSValue stack = JS_GetPropertyStr(js, error, "stack"); - - if (JS_IsNull(stack)) return; - - const char *stackstr = JS_ToCString(js,stack); - - log_print(stackstr); - - JS_FreeCString(js,stackstr); - JS_FreeValue(js,stack); - JS_FreeValue(js, error); + call_callee(&stacktrace_callee); + return; } void js_dump_stack() { + js_stacktrace(); + return; + JSValue exception = JS_GetException(js); if (JS_IsNull(exception)) return; JSValue val = JS_GetPropertyStr(js, exception, "stack"); @@ -94,7 +93,7 @@ int js_print_exception(JSValue v) const char *name = JS_ToCString(js, JS_GetPropertyStr(js, exception, "name")); const char *msg = JS_ToCString(js, JS_GetPropertyStr(js, exception, "message")); const char *stack = JS_ToCString(js, val); - YughError("%s :: %s\n%s", name, msg, stack); + YughWarn("%s :: %s\n%s", name, msg, stack); JS_FreeCString(js, name); JS_FreeCString(js, msg); diff --git a/source/engine/script.h b/source/engine/script.h index 80e2b45..49997e3 100644 --- a/source/engine/script.h +++ b/source/engine/script.h @@ -12,6 +12,9 @@ struct callee { JSValue obj; }; +extern struct callee stacktrace_callee; +extern JSValue num_cache[100]; + void script_startup(); void script_init(); void script_run(const char *script); diff --git a/source/engine/sprite.c b/source/engine/sprite.c index 4672b78..d5bf502 100644 --- a/source/engine/sprite.c +++ b/source/engine/sprite.c @@ -24,7 +24,7 @@ int make_sprite(int go) struct sprite sprite = { .color = {1.f, 1.f, 1.f}, .size = {1.f, 1.f}, - .tex = texture_loadfromfile("ph.png"), + .tex = texture_loadfromfile(NULL), .go = go, .next = -1, .layer = 0, diff --git a/source/engine/texture.c b/source/engine/texture.c index f275dd4..f76980f 100644 --- a/source/engine/texture.c +++ b/source/engine/texture.c @@ -17,9 +17,17 @@ static struct { } *texhash = NULL; struct Texture *tex_default; + +struct Texture *texture_notex() +{ + return texture_pullfromfile("./icons/no_tex.png"); +} + /* If an empty string or null is put for path, loads default texture */ struct Texture *texture_pullfromfile(const char *path) { + if (!path) return texture_notex(); + int index = shgeti(texhash, path); if (index != -1) return texhash[index].value; @@ -48,8 +56,7 @@ struct Texture *texture_pullfromfile(const char *path) if (data == NULL) { YughError("STBI failed to load file %s with message: %s\nOpening default instead.", path, stbi_failure_reason()); - print_stacktrace(); - return texture_pullfromfile("./icons/no_tex.png"); + return texture_notex(); } tex->data = data; diff --git a/source/engine/timer.c b/source/engine/timer.c index afc7e41..ac3ccfa 100644 --- a/source/engine/timer.c +++ b/source/engine/timer.c @@ -21,7 +21,7 @@ void check_timer(struct timer *t, double dt) return; } - t->on = 0; + timer_pause(t); return; } } diff --git a/source/engine/yugine.c b/source/engine/yugine.c index ec555b7..db2f210 100644 --- a/source/engine/yugine.c +++ b/source/engine/yugine.c @@ -28,6 +28,7 @@ #include #include +#include #include "string.h" @@ -73,8 +74,7 @@ void print_stacktrace() for (int i = 0; i < size; i++) YughCritical(stackstr[i]); - js_dump_stack(); - + js_stacktrace(); } void seghandle(int sig) { diff --git a/source/scripts/base.js b/source/scripts/base.js index 9d44b49..8814d65 100644 --- a/source/scripts/base.js +++ b/source/scripts/base.js @@ -435,6 +435,8 @@ Math.lerp = function (s, f, dt) { return s + (Math.clamp(dt, 0, 1) * (f - s)); }; +Math.random_range = function(min,max) { return Math.random() * (max-min) + min; }; + Math.snap = function(val, grid) { if (!grid || grid === 1) return Math.round(val); diff --git a/source/scripts/components.js b/source/scripts/components.js index b93ed42..70a20ae 100644 --- a/source/scripts/components.js +++ b/source/scripts/components.js @@ -26,7 +26,9 @@ var component = { var sprite = clone(component, { name: "sprite", - path: "", + _path: "", + get path() { return this._path; }, + set path(x) { this._path = x; this.load_img(x); }, _pos: [0, 0], get layer() { if (!this.gameobject) @@ -101,7 +103,6 @@ var sprite = clone(component, { if (!this.hasOwn('id')) return; cmd(60, this.id, this.layer); cmd(37, this.id, this.pos); - cmd(12, this.id, this.path, this.rect); }, load_img(img) { @@ -135,11 +136,10 @@ var char2d = clone(sprite, { make(go) { var char = clone(this); char.curplaying = char.anims.array()[0]; - Object.defineProperty(char, 'id', {value:make_sprite(go,this.path,this.pos)}); + Object.defineProperty(char, 'id', {value:make_sprite(go,char.curplaying.path,this.pos)}); char.frame = 0; - char.timer = timer.make(char.advance, 1/char.curplaying.fps, char); + char.timer = timer.make(char.advance.bind(char), 1/char.curplaying.fps); char.timer.loop = true; - char.rect = char.frame2rect(char.curplaying.frames, char.frame); char.setsprite(); return char; }, @@ -152,7 +152,10 @@ var char2d = clone(sprite, { return; } - if (this.curplaying === this.anims[name]) return; + if (this.curplaying === this.anims[name]) { + this.timer.start(); + return; + } this.curplaying = this.anims[name]; this.timer.time = 1/this.curplaying.fps; @@ -170,6 +173,9 @@ var char2d = clone(sprite, { advance() { this.frame = (this.frame + 1) % this.curplaying.frames; this.setsprite(); + + if (this.frame === 0 && !this.curplaying.loop) + this.timer.pause(); }, devance() { @@ -177,15 +183,19 @@ var char2d = clone(sprite, { if (this.frame === -1) this.frame = this.curplaying.frames-1; this.setsprite(); }, + + setframe(frame) { + this.frame = frame; + this.setsprite(); + }, pause() { this.timer.pause(); }, stop() { - this.frame = 0; + this.setframe(0); this.timer.stop(); - this.setsprite(); }, kill() { diff --git a/source/scripts/debug.js b/source/scripts/debug.js index a47fcad..dacb5ec 100644 --- a/source/scripts/debug.js +++ b/source/scripts/debug.js @@ -48,16 +48,6 @@ var Debug = { register_debug(fn,obj); }, - print_callstack() { - for (var i = -3;; i--) { - var t = Duktape.act(i); - if (!t) break; - var file = t.function ? t.function.fileName : ""; - var line = t.lineNumber; - Log.info(file + ":" + line); - } - }, - line(points, color, type) { if (!type) type = 0; diff --git a/source/scripts/diff.js b/source/scripts/diff.js index 1d1f611..05444d3 100644 --- a/source/scripts/diff.js +++ b/source/scripts/diff.js @@ -82,11 +82,14 @@ function unmerge(target, source) { /* Deeply merge two objects, not clobbering objects on target with objects on source */ function deep_merge(target, source) { + Log.warn("Doing a deep merge ..."); for (var key in source) { - if (typeof source[key] === 'object' && !Array.isArray(source[key])) + if (typeof source[key] === 'object' && !Array.isArray(source[key])) { deep_merge(target[key], source[key]); - else + } + else { target[key] = source[key]; + } } }; diff --git a/source/scripts/editor.js b/source/scripts/editor.js index 4cde8c5..5dcdd3f 100644 --- a/source/scripts/editor.js +++ b/source/scripts/editor.js @@ -3,6 +3,12 @@ selectable */ +var required_files = ["proto.json"]; + +required_files.forEach(x => { + if (!IO.exists(x)) slurpwrite("", x); +}); + var editor_level = Level.create(); var editor_camera = editor_level.spawn(camera2d); editor_camera.save = false; @@ -221,7 +227,7 @@ var editor = { if (this.sel_comp) return; if (!this.selectlist.empty) { - this.selectlist.forEach(function(x) { x.draw_layer++; }); + this.selectlist.forEach(x => x.draw_layer++); return; } @@ -246,16 +252,21 @@ var editor = { save_proto() { if (this.selectlist.length !== 1) return; - var protos = JSON.parse(slurp("proto.json")); + Log.warn(`Saving prototype ${this.selectlist[0].toString()}`); + var protos = JSON.parse(IO.slurp("proto.json")); var tobj = this.selectlist[0].prop_obj(); var pobj = this.selectlist[0].__proto__.prop_obj(); + Log.warn("Going to deep merge."); deep_merge(pobj, tobj); + Log.warn("Finished deep merge."); pobj.from = this.selectlist[0].__proto__.from; + protos[this.selectlist[0].__proto__.name] = pobj; + Log.warn(JSON.stringify(protos)); slurpwrite(JSON.stringify(protos, null, 2), "proto.json"); /* Save object changes to parent */ @@ -265,11 +276,12 @@ var editor = { unmerge(this.selectlist[0], tobj); /* Now sync all objects */ - Game.objects.forEach(function(x) { x.sync(); }); + Game.objects.forEach(x => x.sync()); + }, save_prototypes() { - slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); + save_gameobjects_as_prototypes(); }, /* Save the selected object as a new prototype, extending the chain */ @@ -278,7 +290,6 @@ var editor = { Log.info("Already an object with name '" + name + "'. Choose another one."); return; } - var newp = this.selectlist[0].__proto__.clone(name); for (var key in newp) @@ -535,7 +546,7 @@ var editor = { }, input_delete_pressed() { - this.selectlist.forEach(function(x) { x.kill(); }); + this.selectlist.forEach(x => x.kill()); this.unselect(); }, diff --git a/source/scripts/engine.js b/source/scripts/engine.js index 39fbcdd..86b3e38 100644 --- a/source/scripts/engine.js +++ b/source/scripts/engine.js @@ -1,3 +1,5 @@ +"use strict"; + var Log = { set level(x) { cmd(92,x); }, get level() { return cmd(93); }, @@ -44,6 +46,7 @@ var Log = { }, stack(skip = 0) { + Log.warn("Printing stack"); var stack = (new Error()).stack; var n = stack.next('\n',0)+1; for (var i = 0; i < skip; i++) @@ -53,6 +56,7 @@ var Log = { }, }; + var files = {}; function load(file) { var modtime = cmd(0, file); @@ -162,7 +166,7 @@ var Yugine = { }; var timer = { - make(fn, secs, obj, loop) { + make(fn, secs,obj,loop) { if (secs === 0) { fn.call(obj); return; @@ -170,23 +174,17 @@ var timer = { var t = clone(this); t.id = make_timer(fn, secs, obj); - - if (loop) - t.loop = loop; - else - t.loop = 0; - Log.info("Made timer " + t.id); + return t; }, - oneshot(fn, secs, obj) { - var t = clone(this); - var killfn = function() { - fn.call(this); + oneshot(fn, secs,obj) { + var t = this.make(() => { + fn.call(); t.kill(); - }; - t.id = make_timer(killfn,secs,obj); + },secs); t.loop = 0; + t.start(); }, get remain() { return cmd(32, this.id); }, @@ -508,6 +506,8 @@ var Register = { Player.players.forEach(x => x.uncontrol(obj)); }, }; + +Register.unregister_obj(null); register(0, Register.update, Register); register(1, Register.physupdate, Register); register(2, Register.gui, Register); @@ -515,6 +515,7 @@ register(3, Register.nk_gui, Register); register(6, Register.debug, Register); register(7, Register.kbm_input, Register); register(8, Register.gamepad_input, Register); +register(9, Log.stack, this); Register.gamepad_playermap[0] = Player.players[0]; @@ -576,7 +577,13 @@ var Signal = { var IO = { exists(file) { return cmd(65, file);}, - slurp(file) { return cmd(38,file); }, + slurp(file) { + if (!this.exists(file)) { + Log.warn(`File ${file} does not exist; can't slurp.`); + return ""; + } + return cmd(38,file); + }, slurpwrite(str, file) { return cmd(39, str, file); }, extensions(ext) { return cmd(66, "." + ext); }, }; @@ -1442,6 +1449,7 @@ var gameobject = { }, kill() { + Log.warn(`Killing ${this.toString()}`); cmd(2, this.body); delete Game.objects[this.body]; @@ -1613,7 +1621,7 @@ function add_sync_prop(obj, prop, syncfn) { function load_configs(file) { var configs = JSON.parse(IO.slurp(file)); for (var key in configs) { - Object.assign(this[key], configs[key]); + Object.assign(globalThis[key], configs[key]); } Collision.sync(); @@ -1653,7 +1661,7 @@ function save_game_configs() { Game.objects.forEach(function(x) { x.sync(); }); }; -Collision = { +var Collision = { types: {}, num: 10, set_collide(a, b, x) { @@ -1718,3 +1726,38 @@ var camera2d = gameobject.clone("camera2d", { win_make(Game.title, Game.resolution[0], Game.resolution[1]); win_icon("icon.png"); + +/* Default objects */ +gameobject.clone("polygon2d", { + polygon2d: polygon2d.clone(), +}); + +gameobject.clone("edge2d", { + edge2d: bucket.clone(), +}); + +gameobject.clone("sprite", { + sprite: sprite.clone(), +}); + +load("config.js"); + +var prototypes = JSON.parse(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]); + } +} + +function save_gameobjects_as_prototypes() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); };