var g_NewIID = 1000; // some arbitrary not-yet-used number var g_NewMTID = 1000; // some arbitrary not-yet-used number var g_ComponentTypes = {}; var g_Components = {}; // Emulate some engine functions: Engine.RegisterComponentType = function(iid, name, ctor) { TS_ASSERT(!g_ComponentTypes[name]); g_ComponentTypes[name] = { "iid": iid, "ctor": ctor }; }; Engine.RegisterSystemComponentType = function(iid, name, ctor) { TS_ASSERT(!g_ComponentTypes[name]); g_ComponentTypes[name] = { "iid": iid, "ctor": ctor }; }; Engine.RegisterInterface = function(name) { global["IID_" + name] = g_NewIID++; }; Engine.RegisterMessageType = function(name) { global["MT_" + name] = g_NewMTID++; }; Engine.QueryInterface = function(ent, iid) { if (g_Components[ent] && g_Components[ent][iid]) return g_Components[ent][iid]; return null; }; Engine.RegisterGlobal = function(name, value) { global[name] = value; }; Engine.DestroyEntity = function(ent) { for (const cid in g_Components[ent]) { const cmp = g_Components[ent][cid]; if (cmp && cmp.Deinit) cmp.Deinit(); } delete g_Components[ent]; // TODO: should send Destroy message }; Engine.PostMessage = function(ent, iid, message) { // TODO: make this send a message if necessary }; Engine.BroadcastMessage = function(iid, message) { // TODO: make this send a message if necessary }; global.ResetState = function() { g_Components = {}; }; global.AddMock = function(ent, iid, mock) { if (!g_Components[ent]) g_Components[ent] = {}; g_Components[ent][iid] = mock; return g_Components[ent][iid]; }; global.DeleteMock = function(ent, iid) { if (!g_Components[ent]) g_Components[ent] = {}; delete g_Components[ent][iid]; }; global.ConstructComponent = function(ent, name, template) { const cmp = new g_ComponentTypes[name].ctor(); Object.defineProperties(cmp, { "entity": { "value": ent, "configurable": false, "enumerable": false, "writable": false }, "template": { "value": template && deepfreeze(clone(template)), "configurable": false, "enumerable": false, "writable": false }, "_cmpName": { "value": name, "configurable": false, "enumerable": false, "writable": false } }); cmp.Init?.(); if (!g_Components[ent]) g_Components[ent] = {}; g_Components[ent][g_ComponentTypes[name].iid] = cmp; return cmp; }; /** * A simple Spy proxy that tracks and forward function calls. * NB: this immediately replaces obj's func. */ global.Spy = function(obj, func) { this._called = 0; this._callargs = []; const og_func = obj[func]; const spy = (...args) => { ++this._called; this._callargs.push(args); return og_func.apply(obj, args); }; obj[func] = spy; this._reset = () => { this._called = 0; this._callargs = []; }; return this; }; global.SerializationCycle = function(cmp) { let data; if (typeof cmp.Serialize === "function") data = cmp.Serialize(); else { data = {}; for (const att of cmp) if (Object.hasOwn(cmp, att)) data[att] = cmp[att]; } const newCmp = ConstructComponent(cmp.entity, cmp._cmpName, cmp.template); if (typeof newCmp.Deserialize === "function") newCmp.Deserialize(data); else for (const att in data) newCmp[att] = data[att]; return newCmp; };