actor delay

This commit is contained in:
John Alanbrook 2023-11-29 23:31:41 +00:00
parent b9db31c84e
commit 03fb016fbc
10 changed files with 98 additions and 211 deletions

View file

@ -3,3 +3,6 @@
Entities are defined by creating a .jso script in your game directory. Variants of the original entity can be created by defining a .json file, typically done via the provided editor. The original entity is known as its ur-type. If you create a player.jso file in your game directory, a player can be spawned by saying Primum.spawn(ur.player). Entities are defined by creating a .jso script in your game directory. Variants of the original entity can be created by defining a .json file, typically done via the provided editor. The original entity is known as its ur-type. If you create a player.jso file in your game directory, a player can be spawned by saying Primum.spawn(ur.player).
An entity which differs from its ur will have an asterisk * next to its name. An entity which differs from its ur will have an asterisk * next to its name.
## Ur types

View file

@ -2,6 +2,10 @@
Primum programs are organized into two different types of source files: scripts and entities. Scripts end with .js, entities end with .jso. Primum programs are organized into two different types of source files: scripts and entities. Scripts end with .js, entities end with .jso.
Actors can be created with jso files. Make one by calling 'actor.spawn(file)'.
Entities are specialized actors, that are in the world of the computer game. While calling delay on actor causes a delay relative to real-life time, delay on an entity causes a delay relative to the game world time.
## Scripts ## Scripts
Script hooks exist to allow to modification of the game. Script hooks exist to allow to modification of the game.

View file

@ -941,6 +941,19 @@ Object.defineProperty(Array.prototype, 'lerp', {
Math.lerp = function(s,f,t) { return (f-s)*t + s; }; Math.lerp = function(s,f,t) { return (f-s)*t + s; };
Math.grab_from_points = function(pos, points, slop) {
var shortest = slop;
var idx = -1;
points.forEach(function(x,i) {
if (Vector.length(pos.sub(x)) < shortest) {
shortest = Vector.length(pos.sub(x));
idx = i;
}
});
return idx;
};
Number.prec = function(num) Number.prec = function(num)
{ {
return parseFloat(num.toFixed(3)); return parseFloat(num.toFixed(3));

View file

@ -533,7 +533,7 @@ polygon2d.inputs.lm = function(){};
polygon2d.inputs.lm.released = function(){}; polygon2d.inputs.lm.released = function(){};
polygon2d.inputs['C-M-lm'] = function() { polygon2d.inputs['C-M-lm'] = function() {
var idx = grab_from_points(Mouse.worldpos, this.points.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(Mouse.worldpos, this.points.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return; if (idx === -1) return;
this.points.splice(idx, 1); this.points.splice(idx, 1);
}; };
@ -761,7 +761,7 @@ bucket.inputs['C-b'] = function() { this.type = Spline.type.bezier; this.looped
bucket.inputs['C-b'].doc = "Set spline to bezier."; bucket.inputs['C-b'].doc = "Set spline to bezier.";
bucket.inputs['C-M-lm'] = function() { bucket.inputs['C-M-lm'] = function() {
var idx = grab_from_points(Mouse.worldpos, this.cpoints.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(Mouse.worldpos, this.cpoints.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return; if (idx === -1) return;
this.cpoints = this.cpoints.newfirst(idx); this.cpoints = this.cpoints.newfirst(idx);
@ -784,7 +784,7 @@ bucket.inputs['C-lm'] = function() {
bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position."; bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
bucket.inputs['C-M-lm'] = function() { bucket.inputs['C-M-lm'] = function() {
var idx = grab_from_points(Mouse.worldpos, this.cpoints.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(Mouse.worldpos, this.cpoints.map(p => this.gameobject.this2world(p)), 25);
if (idx < 0 || idx > this.cpoints.length) return; if (idx < 0 || idx > this.cpoints.length) return;

View file

@ -1,6 +1,6 @@
var Gizmos = { var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) { pick_gameobject_points(worldpos, gameobject, points) {
var idx = grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25); var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25);
if (idx === -1) return undefined; if (idx === -1) return undefined;
return points[idx]; return points[idx];
}, },
@ -122,7 +122,7 @@ Debug.Options.Color = {
var Gizmos = { var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) { pick_gameobject_points(worldpos, gameobject, points) {
var idx = grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25); var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25);
if (idx === -1) return null; if (idx === -1) return null;
return points[idx]; return points[idx];
}, },
@ -266,12 +266,7 @@ Time.doc.play = "Resume the game after using Time.pause.";
Player.players[0].control(DebugControls); Player.players[0].control(DebugControls);
Register.gui.register(Debug.draw, Debug); Register.gui.register(Debug.draw, Debug);
var console = {}; var console = Object.create(Log);
console.log = Log.say;
console.info = Log.info;
console.warn = Log.warn;
console.error = Log.error;
console.stack = Log.stack;
console.clear = function() console.clear = function()
{ {
cmd(146); cmd(146);

View file

@ -70,112 +70,26 @@ var Device = {
load("scripts/gui.js"); load("scripts/gui.js");
var ctimer = {
make(fn, secs,obj,loop,app) {
obj ??= globalThis;
app ??= false;
if (secs === 0) {
fn.call(obj);
return;
}
var t = Object.create(this);
t.id = make_timer(fn, secs, app, obj);
t.loop = loop;
t.pause();
return t;
},
oneshot(fn, secs,obj, app) {
app ??= false;
var t = this.make(fn, secs, obj, 0, app);
t.start();
return t;
},
get remain() { return cmd(32, this.id); },
get on() { return cmd(33, this.id); },
get loop() { return cmd(34, this.id); },
set loop(x) { cmd(35, this.id, x); },
start() { cmd(26, this.id); },
stop() { cmd(25, this.id); },
pause() { cmd(24, this.id); },
kill() { if (this.dead) return; cmd(27, this.id); this.dead = true; },
set time(x) { cmd(28, this.id, x); },
get time() { return cmd(29, this.id); },
get pct() { return this.remain / this.time; },
};
var timer = { var timer = {
time: 1,
remain: 1,
loop: false,
on: false,
apptime: false, /* If true, based on app's time instead of game world time */
start() {
this.on = true;
},
restart() {
this.remain = this.time;
this.start();
},
update(dt) { update(dt) {
if (!this.on) return;
this.remain -= dt; this.remain -= dt;
if (this.remain <= 0) if (this.remain <= 0)
this.fire(); this.fire();
}, },
fire() {
this.fn();
if (this.loop)
this.restart();
},
pct() { return 1 - (this.remain / this.time); },
kill() { kill() {
Register.unregister_obj(this); Register.unregister_obj(this);
}, },
delay(fn, secs, desc) { delay(fn, secs) {
var t = timer.make(fn,secs,desc);
t.loop = false;
t.restart();
return t;
t.fn = function() { fn(); t.kill(); };
return function() { t.kill(); };
},
oneshot(fn, secs, obj, desc) {
this.delay(fn,secs,desc);
},
make(fn, secs, desc) {
var t = Object.create(this); var t = Object.create(this);
Object.assign(t, desc);
t.time = secs; t.time = secs;
t.remain = secs; t.remain = secs;
t.fn = fn; t.fire = function() { fn(); t.kill(); };
Register.update.register(t.update, t); Register.update.register(t.update, t);
return t; return function() { t.kill(); };
}, },
}; };
timer.toJSON = function() { return undefined; };
timer.doc = {
doc: "Quickly make timers to fire off events once or multiple times.",
oneshot: "Executes a given function after the given number of seconds.",
make: "Returns a timer that can be handled and reused.",
start: "Starts the timer.",
stop: "Stops the timer.",
loop: "Set to true for the timer to repeat when done.",
kill: "Destroys the timer.",
pct: "Get the percentange the timer is complete.",
time: "Set or get the amount of time this timer waits to execute. Does not reset the time, so if it is set to below the elapsed time it will execute immediately.",
remain: "The time remianing before the function is executed.",
};
load("scripts/animation.js"); load("scripts/animation.js");
@ -208,20 +122,6 @@ function screen2world(screenpos) {
function world2screen(worldpos) { return Game.camera.world2view(worldpos); } function world2screen(worldpos) { return Game.camera.world2view(worldpos); }
var Register = { var Register = {
inloop: false,
loopcbs: [],
finloop() {
this.loopcbs.forEach(x => x());
this.loopcbs = [];
},
wraploop(loop) {
this.inloop = true;
loop();
this.inloop = false;
this.finloop();
},
kbm_input(mode, btn, state, ...args) { kbm_input(mode, btn, state, ...args) {
if (state === 'released') { if (state === 'released') {
btn = btn.split('-').last; btn = btn.split('-').last;
@ -321,6 +221,7 @@ var Register = {
}; };
Register.add_cb(0, "update").doc = "Called once per frame."; Register.add_cb(0, "update").doc = "Called once per frame.";
Register.add_cb(11, "appupdate");
Register.add_cb(1, "physupdate"); Register.add_cb(1, "physupdate");
Register.add_cb(2, "gui"); Register.add_cb(2, "gui");
Register.add_cb(6, "debug"); Register.add_cb(6, "debug");
@ -557,17 +458,16 @@ load("scripts/entity.js");
function world_start() { function world_start() {
globalThis.Primum = Object.create(gameobject); globalThis.Primum = Object.create(gameobject);
Primum.objects = {}; Primum.objects = {};
Primum._ed = { Primum._ed = {
selectable:false, selectable:false,
check_dirty() {}, check_dirty() {},
dirty:false, dirty:false,
namestr(){}, namestr(){},
}; };
Primum.toString = function() { return "Primum"; }; Primum.toString = function() { return "Primum"; };
Primum.ur = undefined; Primum.ur = undefined;
Game.view_camera(Primum.spawn(ur.camera2d));
} }
load("scripts/physics.js"); load("scripts/physics.js");

View file

@ -1,15 +1,3 @@
function grab_from_points(pos, points, slop) {
var shortest = slop;
var idx = -1;
points.forEach(function(x,i) {
if (Vector.length(pos.sub(x)) < shortest) {
shortest = Vector.length(pos.sub(x));
idx = i;
}
});
return idx;
};
var actor = {}; var actor = {};
actor.spawn = function(script, config){ actor.spawn = function(script, config){
if (typeof script !== 'string') return; if (typeof script !== 'string') return;
@ -19,21 +7,54 @@ actor.spawn = function(script, config){
if (typeof config === 'object') if (typeof config === 'object')
Object.merge(padawan, config); Object.merge(padawan, config);
padawan.padawans = [];
padawan.timers = [];
padawan.master = this;
Object.hide(padawan, "master","timers");
this.padawans.push(padawan);
return padawan; return padawan;
}; };
actor.die = function(actor){ actor.die = function(actor){
}; };
actor.kill = function(actor){ actor.timers = [];
this.timers.forEach(t => t()); actor.kill = function(){
this.timers = undefined; this.timers.forEach(t => t.kill());
this.master.remove(this);
this.padawans.forEach(p => p.kill());
this.__dead__ = true;
}; };
actor.toJSON = function() {
if (this.__dead__) return undefined;
return this;
}
actor.delay = function(fn, seconds) { actor.delay = function(fn, seconds) {
var t = timer.delay(fn.bind(this), seconds, true); var t = Object.create(timer);
t.remain = seconds;
t.kill = () => {
timer.kill.call(t);
this.timers.remove(t);
}
t.fire = () => {
if (this.__dead__) return;
fn();
t.kill();
};
Register.appupdate.register(t.update, t);
this.timers.push(t); this.timers.push(t);
return function() { t.kill(); }; return function() { t.kill(); };
}; };
actor.clock = function(fn){}; actor.clock = function(fn){};
actor.master = undefined;
actor.padawans = [];
actor.remaster = function(to){
this.master.padawans.remove(this);
this.master = to;
to.padawans.push(this);
};
var gameobject = { var gameobject = {
full_path() { full_path() {
@ -148,7 +169,7 @@ var gameobject = {
set timescale(x) { cmd(168,this.body,x); }, set timescale(x) { cmd(168,this.body,x); },
get timescale() { return cmd(169,this.body); }, get timescale() { return cmd(169,this.body); },
set phys(x) { set_body(1, this.body, x); }, set phys(x) { console.warn(`Setting phys to ${x}`); set_body(1, this.body, x); },
get phys() { return q_body(0,this.body); }, get phys() { return q_body(0,this.body); },
get velocity() { return q_body(3, this.body); }, get velocity() { return q_body(3, this.body); },
set velocity(x) { set_body(9, this.body, x); }, set velocity(x) { set_body(9, this.body, x); },
@ -490,6 +511,7 @@ var gameobject = {
} }
delete this.components; delete this.components;
// q_body(8,this.body);
this.clear(); this.clear();
@ -748,26 +770,12 @@ prototypes.from_file = function(file)
prototypes.list.push(urpath); prototypes.list.push(urpath);
newur.toString = function() { return urpath; }; newur.toString = function() { return urpath; };
ur[urpath] = newur; ur[urpath] = newur;
// var urs = urpath.split('.');
// var u = ur;
// for (var i = 0; i < urs.length; i++)
// u = u[urs[i]] = u[urs[i]] || newur;
//Object.dig(ur, urpath, newur);
return newur; return newur;
} }
prototypes.from_file.doc = "Create a new ur-type from a given script file."; prototypes.from_file.doc = "Create a new ur-type from a given script file.";
prototypes.list = []; prototypes.list = [];
prototypes.from_obj = function(name, obj)
{
var newur = Object.copy(gameobject, obj);
prototypes.ur[name] = newur;
prototypes.list.push(name);
newur.toString = function() { return name; };
return prototypes.ur[name];
}
prototypes.list_ur = function() prototypes.list_ur = function()
{ {
var list = []; var list = [];
@ -844,44 +852,6 @@ prototypes.generate_ur = function(path)
var ur = prototypes.ur; var ur = prototypes.ur;
prototypes.from_obj("camera2d", {
phys: Physics.kinematic,
speed: 300,
get zoom() {
var z = Game.native.y / Window.dimensions.y;
return cmd(135) / z;
},
set zoom(x) {
x = Math.clamp(x,0.1,10);
var z = Game.native.y / Window.dimensions.y;
z *= x;
cmd(62, z);
},
realzoom() {
return cmd(135);
},
speedmult: 1.0,
selectable: false,
dir_view2world(dir) {
return dir.scale(this.realzoom());
},
view2world(pos) {
return cmd(137,pos);
},
world2view(pos) {
return cmd(136,pos);
},
});
prototypes.from_obj("arena", {});
prototypes.resavi = function(ur, path) prototypes.resavi = function(ur, path)
{ {
if (!ur) return path; if (!ur) return path;

View file

@ -677,9 +677,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 36: case 36:
id2go(js2int(argv[1]))->scale.XY = js2vec2(argv[2]); js2go(argv[1])->scale.XY = js2vec2(argv[2]);
gameobject_apply(id2go(js2int(argv[1]))); gameobject_apply(js2go(argv[1]));
cpSpaceReindexShapesForBody(space, id2go(js2int(argv[1]))->body); cpSpaceReindexShapesForBody(space, js2go(argv[1])->body);
break; break;
case 37: case 37:
@ -700,21 +700,21 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 40: case 40:
id2go(js2int(argv[1]))->filter.categories = js2bitmask(argv[2]); js2go(argv[1])->filter.categories = js2bitmask(argv[2]);
gameobject_apply(id2go(js2int(argv[1]))); gameobject_apply(js2go(argv[1]));
break; break;
case 41: case 41:
id2go(js2int(argv[1]))->filter.mask = js2bitmask(argv[2]); js2go(argv[1])->filter.mask = js2bitmask(argv[2]);
gameobject_apply(id2go(js2int(argv[1]))); gameobject_apply(js2go(argv[1]));
break; break;
case 42: case 42:
ret = bitmask2js(id2go(js2int(argv[1]))->filter.categories); ret = bitmask2js(js2go(argv[1])->filter.categories);
break; break;
case 43: case 43:
ret = bitmask2js(id2go(js2int(argv[1]))->filter.mask); ret = bitmask2js(js2go(argv[1])->filter.mask);
break; break;
case 44: case 44:
@ -1247,7 +1247,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
js2dsp_node(argv[1])->gain = js2number(argv[2]); js2dsp_node(argv[1])->gain = js2number(argv[2]);
break; break;
case 177: case 177:
plugin_node(js2ptr(argv[1]), js2ptr(argv[2])); plugin_node(js2dsp_node(argv[1]), js2dsp_node(argv[2]));
break; break;
case 178: case 178:
ret = num2js(js2dsp_node(argv[1])->pan); ret = num2js(js2dsp_node(argv[1])->pan);
@ -1263,7 +1263,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 182: case 182:
str = js2str(argv[1]); str = js2str(argv[1]);
ret = ptr2js(dsp_source(str)); ret = dsp_node2js(dsp_source(str));
((sound*)js2dsp_node(ret)->data)->hook = JS_DupValue(js,argv[2]); ((sound*)js2dsp_node(ret)->data)->hook = JS_DupValue(js,argv[2]);
break; break;
case 183: case 183:
@ -1629,7 +1629,7 @@ JSValue duk_q_body(JSContext *js, JSValueConst this, int argc, JSValueConst *arg
return JS_NewBool(js, phys2d_in_air(go->body)); return JS_NewBool(js, phys2d_in_air(go->body));
case 8: case 8:
// gameobject_delete(goid); gameobject_delete(go2id(go));
break; break;
} }

View file

@ -4,6 +4,7 @@
struct timer { struct timer {
int timerid; int timerid;
int on; int on;
double interval; // Time of timer double interval; // Time of timer
int repeat; int repeat;
double remain_time; // How much time until the timer executes double remain_time; // How much time until the timer executes

View file

@ -162,6 +162,7 @@ int frame_fps() {
static void process_frame() static void process_frame()
{ {
double elapsed = stm_sec(stm_laptime(&frame_t)); double elapsed = stm_sec(stm_laptime(&frame_t));
script_evalf("Register.appupdate.broadcast(%g);", elapsed);
call_stack(); call_stack();
// ds_advance(bjork, elapsed); // ds_advance(bjork, elapsed);
input_poll(0); input_poll(0);