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
* 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
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:
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;
case 119:
@ -1050,6 +1050,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 120:
ret = str2js(engine_info());
break;
case 121:
return num2js(get_timescale());
break;
}
if (str)

View file

@ -309,3 +309,8 @@ void sim_step() {
void set_timescale(float 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); });
}

View file

@ -61,7 +61,7 @@ var Debug = {
},
register_call(fn, obj) {
register_debug(fn,obj);
Register.debug.register(fn,obj);
},
line(points, color, type, thickness) {
@ -103,8 +103,8 @@ var Debug = {
: Game.stepping() ?
"STEP" :
Game.paused() ?
"PAUSED" :
"STOPPED", [0, 0], 1);
"PAUSED; EDITING" :
"EDIT", [0, 0], 1);
},
};
@ -223,6 +223,7 @@ var Nuke = {
Object.defineProperty(Nuke, "curwin", {enumerable:false});
Object.defineProperty(Nuke, "defaultrect", {enumerable:false});
/* These controls are available during editing, and during play of debug builds */
var DebugControls = {};
DebugControls.inputs = {};
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;
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 = {
set timescale(x) { cmd(3, x); },
get timescale() { return cmd(121); },
set updateMS(x) { cmd(6, x); },
set physMS(x) { cmd(7, x); },
set renderMS(x) { cmd(5, x); },
};
set_pawn(DebugControls);
register_gui(Debug.draw, Debug);
Player.players[0].control(DebugControls);
Register.gui.register(Debug.draw, Debug);

View file

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

View file

@ -146,40 +146,6 @@ var Physics = {
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 = {
white: [255,255,255,255],
blue: [84,110,255,255],
@ -612,9 +578,9 @@ var Tween = {
defn.restart = function() { defn.accum = 0; };
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;
},
@ -662,7 +628,6 @@ var Tween = {
},
};
var animation = {
time: 0,
loop: false,
@ -672,7 +637,7 @@ var animation = {
create() {
var anim = Object.create(animation);
register_update(anim.update, anim);
Register.update.register(anim.update, anim);
return anim;
},
@ -736,16 +701,6 @@ var animation = {
},
};
var sound = {
play() {
this.id = cmd(14,this.path);
},
stop() {
},
};
var Music = {
play(path) {
Log.info("Playing " + path);
@ -769,6 +724,7 @@ var Sound = {
var s = Object.create(sound);
s.path = file;
s.play();
// this.id = cmd(14,file);
return s;
},
@ -851,6 +807,18 @@ var Input = {
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) {
if (!('inputs' in pawn)) return;
var str = "";
@ -957,6 +925,15 @@ var Player = {
uncontrol(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++) {
@ -966,18 +943,6 @@ for (var i = 0; i < 4; i++) {
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 = {
inloop: false,
loopcbs: [],
@ -993,27 +958,6 @@ var Register = {
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) {
var input = `${src}_${btn}_${state}`;
Player.players[0].input(input, ...args);
@ -1038,7 +982,7 @@ var Register = {
var player = this.gamepad_playermap[pad];
if (!player) return;
var statestr = state2str(state);
var statestr = Input.state2str(state);
var rawfn = `gamepad_${btn}_${statestr}`;
player.input(rawfn, ...args);
@ -1049,26 +993,13 @@ var Register = {
});
},
debugs: [],
debug() {
this.debugs.forEach(x => x[0].call(x[1]));
},
unregister_obj(obj) {
this.updates = this.updates.filter(x => x[1] !== obj);
this.guis = this.guis.filter(x => x[1] !== obj);
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);
Register.registries.forEach(function(x) {
x.clear();
});
Player.players.forEach(x => x.uncontrol(obj));
},
draws: [],
draw() {
this.draws.forEach(x => x[0].call(x[1]));
},
endofloop(fn) {
if (!this.inloop)
fn();
@ -1076,68 +1007,57 @@ var Register = {
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(1, Register.physupdate, Register);
register(2, Register.gui, Register);
register(3, Register.nk_gui, Register);
register(6, Register.debug, Register);
Register.add_cb(0, "update").doc = "Called once per frame.";
Register.add_cb(1, "physupdate");
Register.add_cb(2, "gui");
Register.add_cb(3, "nk_gui");
Register.add_cb(6, "debug");
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(10, Register.draw, Register);
Register.gamepad_playermap[0] = Player.players[0];
function register_update(fn, obj) {
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);
}
Register.update.register(Yugine.exec, Yugine);
Player.players[0].control(GUI);
@ -1177,6 +1097,9 @@ var Window = {
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) {
Window.width = w;
Window.height = h;
@ -1196,9 +1119,6 @@ var IO = {
extensions(ext) { return cmd(66, "." + ext); },
};
function slurp(file) { return IO.slurp(file);}
function slurpwrite(str, file) { return IO.slurpwrite(str, file); }
function reloadfiles() {
Object.keys(files).forEach(function (x) { load(x); });
}
@ -1413,7 +1333,7 @@ var Level = {
Object.assign(this, extern);
if (typeof update === 'function')
register_update(update, this);
Register.update.register(update, this);
if (typeof gui === 'function')
register_gui(gui, this);
@ -1620,7 +1540,7 @@ var Level = {
loadlevel(file) {
var lvl = Level.loadfile(file);
if (lvl && sim_playing())
if (lvl && Game.playing())
lvl.start();
return lvl;
@ -1983,7 +1903,9 @@ var gameobject = {
flipy: false,
body: -1,
controlled: false,
get controlled() {
return Player.obj_controlled(this);
},
set_center(pos) {
var change = pos.sub(this.pos);
@ -2066,16 +1988,6 @@ var 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 */
get boundingbox() {
var boxes = [];
@ -2187,10 +2099,10 @@ var gameobject = {
Register.unregister_obj(this);
if (typeof obj.update === 'function')
register_update(obj.update, obj);
Register.update.register(obj.update, obj);
if (typeof obj.physupdate === 'function')
register_physupdate(obj.physupdate, obj);
Register.physupdate.register(obj.physupdate, obj);
if (typeof obj.collide === 'function')
obj.register_hit(obj.collide, obj);
@ -2199,10 +2111,10 @@ var gameobject = {
obj.register_separate(obj.separate, obj);
if (typeof obj.draw === 'function')
register_draw(obj.draw,obj);
Register.draw.register(obj.draw,obj);
if (typeof obj.debug === 'function')
register_debug(obj.debug, obj);
Register.debug.register(obj.debug, obj);
obj.components.forEach(function(x) {
if (typeof x.collide === 'function')
@ -2418,7 +2330,6 @@ Yugine.view_camera = function(cam)
Yugine.view_camera(World.spawn(camera2d));
win_make(Game.title, Game.resolution[0], Game.resolution[1]);
//win_icon("icon.png");
/* Default objects */
gameobject.clone("polygon2d", {
@ -2439,7 +2350,7 @@ if (IO.exists("config.js"))
var prototypes = {};
if (IO.exists("proto.json"))
prototypes = JSON.parse(slurp("proto.json"));
prototypes = JSON.parse(IO.slurp("proto.json"));
for (var key in prototypes) {
if (key in gameobjects)

View file

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