Rework Register and Player objects to remove some global functions

This commit is contained in:
John Alanbrook 2023-08-28 22:00:53 +00:00
parent aad89926d9
commit 9dc04f6ce7
8 changed files with 135 additions and 200 deletions

View file

@ -25,9 +25,17 @@ associated script file can access.
* F6 game.lvl * F6 game.lvl
* F7 Currently edited level * F7 Currently edited level
While playing ... ## Playing, editing, debugging
* F7 Stop Playing is playing your game. Controls are what are specified for your game.
In debug builds, additional debug controls are available. For example, F12 brings up GUI debug boxes. C-M-f puts you into a flying camera mode, without pausing your game.
The game can be paused to edit it. Most editor controls are available here, all of them essentially except for loading new levels, clearing the level, etc. An object can be clicked on and edited, objects can be moved, etc.
A prefab can be opened up to edit on its own, without breaking the currently played level.
In edit mode, there are no running scripts; only editing them.
## Level model ## Level model
The game world is made up of objects. Levels are collections of The game world is made up of objects. Levels are collections of

View file

@ -1039,7 +1039,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 118: case 118:
str = JS_ToCString(js,argv[1]); str = JS_ToCString(js,argv[1]);
return bb2js(text_bb(str, js2number(argv[2]), js2number(argv[3]), 1.0)); ret = bb2js(text_bb(str, js2number(argv[2]), js2number(argv[3]), 1.0));
break; break;
case 119: case 119:
@ -1050,6 +1050,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 120: case 120:
ret = str2js(engine_info()); ret = str2js(engine_info());
break; break;
case 121:
return num2js(get_timescale());
break;
} }
if (str) if (str)

View file

@ -309,3 +309,8 @@ void sim_step() {
void set_timescale(float val) { void set_timescale(float val) {
timescale = val; timescale = val;
} }
double get_timescale()
{
return timescale;
}

View file

@ -701,4 +701,3 @@ function sortpointsccw(points)
return ccw.map(function(x) { return x.add(cm); }); return ccw.map(function(x) { return x.add(cm); });
} }

View file

@ -61,7 +61,7 @@ var Debug = {
}, },
register_call(fn, obj) { register_call(fn, obj) {
register_debug(fn,obj); Register.debug.register(fn,obj);
}, },
line(points, color, type, thickness) { line(points, color, type, thickness) {
@ -103,8 +103,8 @@ var Debug = {
: Game.stepping() ? : Game.stepping() ?
"STEP" : "STEP" :
Game.paused() ? Game.paused() ?
"PAUSED" : "PAUSED; EDITING" :
"STOPPED", [0, 0], 1); "EDIT", [0, 0], 1);
}, },
}; };
@ -223,6 +223,7 @@ var Nuke = {
Object.defineProperty(Nuke, "curwin", {enumerable:false}); Object.defineProperty(Nuke, "curwin", {enumerable:false});
Object.defineProperty(Nuke, "defaultrect", {enumerable:false}); Object.defineProperty(Nuke, "defaultrect", {enumerable:false});
/* These controls are available during editing, and during play of debug builds */
var DebugControls = {}; var DebugControls = {};
DebugControls.inputs = {}; DebugControls.inputs = {};
DebugControls.inputs.f1 = function () { Debug.draw_phys(!Debug.phys_drawing); }; DebugControls.inputs.f1 = function () { Debug.draw_phys(!Debug.phys_drawing); };
@ -245,12 +246,16 @@ Render.normal.doc = "Render mode for enabling all shaders and lighting effects."
DebugControls.inputs['M-2'] = Render.wireframe; DebugControls.inputs['M-2'] = Render.wireframe;
Render.wireframe.doc = "Render mode to see wireframes of all models."; Render.wireframe.doc = "Render mode to see wireframes of all models.";
DebugControls.inputs['C-M-f'] = function() {};
DebugControls.inputs['C-M-f'].doc = "Enter camera fly mode.";
var Time = { var Time = {
set timescale(x) { cmd(3, x); }, set timescale(x) { cmd(3, x); },
get timescale() { return cmd(121); },
set updateMS(x) { cmd(6, x); }, set updateMS(x) { cmd(6, x); },
set physMS(x) { cmd(7, x); }, set physMS(x) { cmd(7, x); },
set renderMS(x) { cmd(5, x); }, set renderMS(x) { cmd(5, x); },
}; };
set_pawn(DebugControls); Player.players[0].control(DebugControls);
register_gui(Debug.draw, Debug); Register.gui.register(Debug.draw, Debug);

View file

@ -531,8 +531,8 @@ var editor = {
this.edit_level.kill(); this.edit_level.kill();
load_configs("game.config"); load_configs("game.config");
Game.play(); Game.play();
unset_pawn(this); Player.players[0].uncontrol(this);
set_pawn(limited_editor); Player.players[0].control(limited_editor);
Register.unregister_obj(this); Register.unregister_obj(this);
}, },
@ -785,13 +785,13 @@ var editor = {
get sel_comp() { return this._sel_comp; }, get sel_comp() { return this._sel_comp; },
set sel_comp(x) { set sel_comp(x) {
if (this._sel_comp) if (this._sel_comp)
unset_pawn(this._sel_comp); Player.players[0].uncontrol(this._sel_comp);
this._sel_comp = x; this._sel_comp = x;
if (this._sel_comp) { if (this._sel_comp) {
Log.info("sel comp is now " + this._sel_comp); Log.info("sel comp is now " + this._sel_comp);
set_pawn(this._sel_comp); Player.players[0].control(this._sel_comp);
} }
}, },
@ -1241,7 +1241,7 @@ editor.inputs['C-space'] = function() {
editor.inputs['C-space'].doc = "Search to execute a specific command."; editor.inputs['C-space'].doc = "Search to execute a specific command.";
editor.inputs['M-m'] = function() { editor.inputs['M-m'] = function() {
// set_pawn(rebinder); // Player.players[0].control(rebinder);
}; };
editor.inputs['M-m'].doc = "Rebind a shortcut. Usage: M-m SHORTCUT TARGET"; editor.inputs['M-m'].doc = "Rebind a shortcut. Usage: M-m SHORTCUT TARGET";
@ -1593,8 +1593,8 @@ var inputpanel = {
this.value = ""; this.value = "";
if (steal) { if (steal) {
this.stolen = steal; this.stolen = steal;
unset_pawn(this.stolen); Player.players[0].uncontrol(this.stolen);
set_pawn(this); Player.players[0].control(this);
} }
this.start(); this.start();
this.keycb(); this.keycb();
@ -1604,9 +1604,9 @@ var inputpanel = {
close() { close() {
unset_pawn(this); Player.players[0].uncontrol(this);
if (this.stolen) { if (this.stolen) {
set_pawn(this.stolen); Player.players[0].control(this.stolen);
this.stolen = null; this.stolen = null;
} }
@ -2457,7 +2457,7 @@ var texgui = clone(inputpanel, {
}); });
var levellistpanel = copy(inputpanel, { var levellistpanel = copy(inputpanel, {
title: "Level list", title: "Level object list",
level: {}, level: {},
start() { start() {
this.level = editor.edit_level; this.level = editor.edit_level;
@ -2521,8 +2521,8 @@ limited_editor.inputs['C-q'] = function()
Game.stop(); Game.stop();
game.stop(); game.stop();
Sound.killall(); Sound.killall();
unset_pawn(limited_editor); Player.players[0].uncontrol(limited_editor);
set_pawn(editor); Player.players[0].control(editor);
register_gui(editor.ed_gui, editor); register_gui(editor.ed_gui, editor);
Debug.register_call(editor.ed_debug, editor); Debug.register_call(editor.ed_debug, editor);
World.kill(); World.kill();
@ -2531,8 +2531,12 @@ limited_editor.inputs['C-q'] = function()
Yugine.view_camera(editor_camera); Yugine.view_camera(editor_camera);
} }
set_pawn(editor); /* This is used for editing during a paused game */
register_gui(editor.ed_gui, editor); var limited_editing = {};
limited_editing.inputs = {};
Player.players[0].control(editor);
Register.gui.register(editor.ed_gui, editor);
Debug.register_call(editor.ed_debug, editor); Debug.register_call(editor.ed_debug, editor);
if (IO.exists("editor.config")) if (IO.exists("editor.config"))

View file

@ -146,40 +146,6 @@ var Physics = {
static: 2, static: 2,
}; };
Physics.stop = function()
{
}
function win_icon(str) {
cmd(90, str);
};
function sim_start() {
Log.warn("Call Game.play() now.");
Game.play();
/*
Game.objects.forEach(function(x) {
if (x.start) x.start(); });
Level.levels.forEach(function(lvl) {
lvl.run();
});
*/
}
function sim_stop() { Log.warn("Call through Game.stop()"); Game.stop();}
function sim_pause() { Log.warn("Call Game.pause()"); Game.pause(); }
function sim_step() { Log.warn("Call Game.step()"); Game.step(); }
function sim_playing() { Log.warn("Call Game.playing"); return Game.playing(); }
function sim_paused() { Log.warn("Call Game.paused"); return Game.paused(); }
function phys_stepping() { Log.warn("Call Game.stepping"); return Game.stepping(); }
function quit() {
Log.warn("Call through Game.quit() now.");
Game.quit();
};
var Color = { var Color = {
white: [255,255,255,255], white: [255,255,255,255],
blue: [84,110,255,255], blue: [84,110,255,255],
@ -612,9 +578,9 @@ var Tween = {
defn.restart = function() { defn.accum = 0; }; defn.restart = function() { defn.accum = 0; };
defn.stop = function() { defn.pause(); defn.restart(); }; defn.stop = function() { defn.pause(); defn.restart(); };
defn.pause = function() { unregister_update(defn.fn); }; defn.pause = function() { Register.update.unregister(defn.fn); };
register_update(defn.fn, defn); Register.update.register(defn.fn, defn);
return defn; return defn;
}, },
@ -662,7 +628,6 @@ var Tween = {
}, },
}; };
var animation = { var animation = {
time: 0, time: 0,
loop: false, loop: false,
@ -672,7 +637,7 @@ var animation = {
create() { create() {
var anim = Object.create(animation); var anim = Object.create(animation);
register_update(anim.update, anim); Register.update.register(anim.update, anim);
return anim; return anim;
}, },
@ -736,16 +701,6 @@ var animation = {
}, },
}; };
var sound = {
play() {
this.id = cmd(14,this.path);
},
stop() {
},
};
var Music = { var Music = {
play(path) { play(path) {
Log.info("Playing " + path); Log.info("Playing " + path);
@ -769,6 +724,7 @@ var Sound = {
var s = Object.create(sound); var s = Object.create(sound);
s.path = file; s.path = file;
s.play(); s.play();
// this.id = cmd(14,file);
return s; return s;
}, },
@ -851,6 +807,18 @@ var Input = {
setnuke() { cmd(78); }, setnuke() { cmd(78); },
}; };
Input.state2str = function(state) {
if (typeof state === 'string') return state;
switch (state) {
case 0:
return "down";
case 1:
return "pressed";
case 2:
return "released";
}
}
Input.print_pawn_kbm = function(pawn) { Input.print_pawn_kbm = function(pawn) {
if (!('inputs' in pawn)) return; if (!('inputs' in pawn)) return;
var str = ""; var str = "";
@ -957,6 +925,15 @@ var Player = {
uncontrol(pawn) { uncontrol(pawn) {
this.pawns = this.pawns.filter(x => x !== pawn); this.pawns = this.pawns.filter(x => x !== pawn);
}, },
obj_controlled(obj) {
for (var p in Player.players) {
if (p.pawns.contains(obj))
return true;
}
return false;
},
}; };
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
@ -966,18 +943,6 @@ for (var i = 0; i < 4; i++) {
Player.players.push(player1); Player.players.push(player1);
} }
function state2str(state) {
if (typeof state === 'string') return state;
switch (state) {
case 0:
return "down";
case 1:
return "pressed";
case 2:
return "released";
}
}
var Register = { var Register = {
inloop: false, inloop: false,
loopcbs: [], loopcbs: [],
@ -992,27 +957,6 @@ var Register = {
this.inloop = false; this.inloop = false;
this.finloop(); this.finloop();
}, },
updates: [],
update(dt) {
this.wraploop(() => this.updates.forEach(x => x[0].call(x[1], dt)));
},
physupdates: [],
physupdate(dt) {
this.wraploop(() => this.physupdates.forEach(x => x[0].call(x[1], dt)));
},
guis: [],
gui() {
this.guis.forEach(x => x[0].call(x[1]));
},
nk_guis: [],
nk_gui() {
this.nk_guis.forEach(x => x[0].call(x[1]));
},
kbm_input(src, btn, state, ...args) { kbm_input(src, btn, state, ...args) {
var input = `${src}_${btn}_${state}`; var input = `${src}_${btn}_${state}`;
@ -1038,7 +982,7 @@ var Register = {
var player = this.gamepad_playermap[pad]; var player = this.gamepad_playermap[pad];
if (!player) return; if (!player) return;
var statestr = state2str(state); var statestr = Input.state2str(state);
var rawfn = `gamepad_${btn}_${statestr}`; var rawfn = `gamepad_${btn}_${statestr}`;
player.input(rawfn, ...args); player.input(rawfn, ...args);
@ -1048,27 +992,14 @@ var Register = {
player.input(`action_${x.name}_${statestr}`, ...args); player.input(`action_${x.name}_${statestr}`, ...args);
}); });
}, },
debugs: [],
debug() {
this.debugs.forEach(x => x[0].call(x[1]));
},
unregister_obj(obj) { unregister_obj(obj) {
this.updates = this.updates.filter(x => x[1] !== obj); Register.registries.forEach(function(x) {
this.guis = this.guis.filter(x => x[1] !== obj); x.clear();
this.nk_guis = this.nk_guis.filter(x => x[1] !== obj); });
this.debugs = this.debugs.filter(x => x[1] !== obj);
this.physupdates = this.physupdates.filter(x => x[1] !== obj);
this.draws = this.draws.filter(x => x[1] !== obj);
Player.players.forEach(x => x.uncontrol(obj)); Player.players.forEach(x => x.uncontrol(obj));
}, },
draws: [],
draw() {
this.draws.forEach(x => x[0].call(x[1]));
},
endofloop(fn) { endofloop(fn) {
if (!this.inloop) if (!this.inloop)
fn(); fn();
@ -1076,68 +1007,57 @@ var Register = {
this.loopcbs.push(fn); this.loopcbs.push(fn);
} }
}, },
clear() {
Register.registries.forEach(function(n) {
n.entries = [];
});
},
registries: [],
add_cb(idx, name) {
var entries = [];
var n = {};
n.register = function(fn, obj) {
entries.push([fn, obj ? obj : null]);
}
n.unregister = function(fn) {
entries = entries.filter(function(f) { return fn === f; });
}
n.broadcast = function() {
entries.forEach(x => x[0].call(x[1]));
}
n.clear = function() {
entries = [];
}
register(idx, n.broadcast, n);
Register[name] = n;
Register.registries.push(n);
return n;
},
}; };
register(0, Register.update, Register); Register.add_cb(0, "update").doc = "Called once per frame.";
register(1, Register.physupdate, Register); Register.add_cb(1, "physupdate");
register(2, Register.gui, Register); Register.add_cb(2, "gui");
register(3, Register.nk_gui, Register); Register.add_cb(3, "nk_gui");
register(6, Register.debug, Register); Register.add_cb(6, "debug");
register(7, Register.kbm_input, Register); register(7, Register.kbm_input, Register);
register(8, Register.gamepad_input, Register); Register.add_cb(8, "gamepad_input");
Register.add_cb(10, "draw");
register(9, Log.stack, this); register(9, Log.stack, this);
register(10, Register.draw, Register);
Register.gamepad_playermap[0] = Player.players[0]; Register.gamepad_playermap[0] = Player.players[0];
function register_update(fn, obj) { Register.update.register(Yugine.exec, Yugine);
Register.updates.push([fn, obj ? obj : null]);
};
function unregister_update(fn) {
Register.updates = Register.updates.filter(function(updatefn) {
return fn === updatefn;
});
};
function register_physupdate(fn, obj) {
Register.physupdates.push([fn, obj ? obj : null]);
};
function register_gui(fn, obj) {
Register.guis.push([fn, obj ? obj : this]);
};
function register_debug(fn, obj) {
Register.debugs.push([fn, obj ? obj : this]);
};
function unregister_gui(fn, obj) {
Register.guis = Register.guis.filter(x => x[0] !== fn || x[1] !== obj);
};
function register_nk_gui(fn, obj) {
Register.nk_guis.push([fn, obj ? obj : this]);
};
function unregister_nk_gui(fn, obj) {
Register.nk_guis = Register.nk_guis.filter(x => x[0] !== fn && x[1] !== obj);
};
function register_draw(fn,obj) {
Register.draws.push([fn, obj ? obj : this]);
}
register_update(Yugine.exec, Yugine);
/* These functions are the "defaults", and give control to player0 */
function set_pawn(obj, player = Player.players[0]) {
player.control(obj);
}
function unset_pawn(obj, player = Player.players[0]) {
player.uncontrol(obj);
}
Player.players[0].control(GUI); Player.players[0].control(GUI);
@ -1177,6 +1097,9 @@ var Window = {
dimensions:[0,0], dimensions:[0,0],
}; };
Window.icon = function(path) { cmd(90, path); };
Window.icon.doc = "Set the icon of the window using the PNG image at path.";
Signal.register("window_resize", function(w, h) { Signal.register("window_resize", function(w, h) {
Window.width = w; Window.width = w;
Window.height = h; Window.height = h;
@ -1196,9 +1119,6 @@ var IO = {
extensions(ext) { return cmd(66, "." + ext); }, extensions(ext) { return cmd(66, "." + ext); },
}; };
function slurp(file) { return IO.slurp(file);}
function slurpwrite(str, file) { return IO.slurpwrite(str, file); }
function reloadfiles() { function reloadfiles() {
Object.keys(files).forEach(function (x) { load(x); }); Object.keys(files).forEach(function (x) { load(x); });
} }
@ -1413,7 +1333,7 @@ var Level = {
Object.assign(this, extern); Object.assign(this, extern);
if (typeof update === 'function') if (typeof update === 'function')
register_update(update, this); Register.update.register(update, this);
if (typeof gui === 'function') if (typeof gui === 'function')
register_gui(gui, this); register_gui(gui, this);
@ -1620,7 +1540,7 @@ var Level = {
loadlevel(file) { loadlevel(file) {
var lvl = Level.loadfile(file); var lvl = Level.loadfile(file);
if (lvl && sim_playing()) if (lvl && Game.playing())
lvl.start(); lvl.start();
return lvl; return lvl;
@ -1983,7 +1903,9 @@ var gameobject = {
flipy: false, flipy: false,
body: -1, body: -1,
controlled: false, get controlled() {
return Player.obj_controlled(this);
},
set_center(pos) { set_center(pos) {
var change = pos.sub(this.pos); var change = pos.sub(this.pos);
@ -2066,16 +1988,6 @@ var gameobject = {
gizmo: "", /* Path to an image to draw for this gameobject */ gizmo: "", /* Path to an image to draw for this gameobject */
set_pawn() {
this.controlled = true;
set_pawn(this);
},
uncontrol() {
if (!this.controlled) return;
unset_pawn(this);
},
/* Bounding box of the object in world dimensions */ /* Bounding box of the object in world dimensions */
get boundingbox() { get boundingbox() {
var boxes = []; var boxes = [];
@ -2187,10 +2099,10 @@ var gameobject = {
Register.unregister_obj(this); Register.unregister_obj(this);
if (typeof obj.update === 'function') if (typeof obj.update === 'function')
register_update(obj.update, obj); Register.update.register(obj.update, obj);
if (typeof obj.physupdate === 'function') if (typeof obj.physupdate === 'function')
register_physupdate(obj.physupdate, obj); Register.physupdate.register(obj.physupdate, obj);
if (typeof obj.collide === 'function') if (typeof obj.collide === 'function')
obj.register_hit(obj.collide, obj); obj.register_hit(obj.collide, obj);
@ -2199,10 +2111,10 @@ var gameobject = {
obj.register_separate(obj.separate, obj); obj.register_separate(obj.separate, obj);
if (typeof obj.draw === 'function') if (typeof obj.draw === 'function')
register_draw(obj.draw,obj); Register.draw.register(obj.draw,obj);
if (typeof obj.debug === 'function') if (typeof obj.debug === 'function')
register_debug(obj.debug, obj); Register.debug.register(obj.debug, obj);
obj.components.forEach(function(x) { obj.components.forEach(function(x) {
if (typeof x.collide === 'function') if (typeof x.collide === 'function')
@ -2418,7 +2330,6 @@ Yugine.view_camera = function(cam)
Yugine.view_camera(World.spawn(camera2d)); Yugine.view_camera(World.spawn(camera2d));
win_make(Game.title, Game.resolution[0], Game.resolution[1]); win_make(Game.title, Game.resolution[0], Game.resolution[1]);
//win_icon("icon.png");
/* Default objects */ /* Default objects */
gameobject.clone("polygon2d", { gameobject.clone("polygon2d", {
@ -2439,7 +2350,7 @@ if (IO.exists("config.js"))
var prototypes = {}; var prototypes = {};
if (IO.exists("proto.json")) if (IO.exists("proto.json"))
prototypes = JSON.parse(slurp("proto.json")); prototypes = JSON.parse(IO.slurp("proto.json"));
for (var key in prototypes) { for (var key in prototypes) {
if (key in gameobjects) if (key in gameobjects)

View file

@ -1,4 +1,4 @@
sim_start(); Game.play();
if (!IO.exists("game.js")) if (!IO.exists("game.js"))