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).
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.
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
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.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)
{
return parseFloat(num.toFixed(3));

View file

@ -533,7 +533,7 @@ polygon2d.inputs.lm = function(){};
polygon2d.inputs.lm.released = 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;
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-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;
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-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;

View file

@ -1,6 +1,6 @@
var Gizmos = {
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;
return points[idx];
},
@ -122,7 +122,7 @@ Debug.Options.Color = {
var Gizmos = {
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;
return points[idx];
},
@ -266,12 +266,7 @@ Time.doc.play = "Resume the game after using Time.pause.";
Player.players[0].control(DebugControls);
Register.gui.register(Debug.draw, Debug);
var console = {};
console.log = Log.say;
console.info = Log.info;
console.warn = Log.warn;
console.error = Log.error;
console.stack = Log.stack;
var console = Object.create(Log);
console.clear = function()
{
cmd(146);

View file

@ -70,112 +70,26 @@ var Device = {
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 = {
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) {
if (!this.on) return;
this.remain -= dt;
if (this.remain <= 0)
this.fire();
},
fire() {
this.fn();
if (this.loop)
this.restart();
},
pct() { return 1 - (this.remain / this.time); },
kill() {
Register.unregister_obj(this);
},
delay(fn, secs, desc) {
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) {
delay(fn, secs) {
var t = Object.create(this);
Object.assign(t, desc);
t.time = secs;
t.remain = secs;
t.fn = fn;
t.fire = function() { fn(); t.kill(); };
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");
@ -208,20 +122,6 @@ function screen2world(screenpos) {
function world2screen(worldpos) { return Game.camera.world2view(worldpos); }
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) {
if (state === 'released') {
btn = btn.split('-').last;
@ -321,6 +221,7 @@ var Register = {
};
Register.add_cb(0, "update").doc = "Called once per frame.";
Register.add_cb(11, "appupdate");
Register.add_cb(1, "physupdate");
Register.add_cb(2, "gui");
Register.add_cb(6, "debug");
@ -557,17 +458,16 @@ load("scripts/entity.js");
function world_start() {
globalThis.Primum = Object.create(gameobject);
Primum.objects = {};
Primum._ed = {
globalThis.Primum = Object.create(gameobject);
Primum.objects = {};
Primum._ed = {
selectable:false,
check_dirty() {},
dirty:false,
namestr(){},
};
Primum.toString = function() { return "Primum"; };
Primum.ur = undefined;
Game.view_camera(Primum.spawn(ur.camera2d));
};
Primum.toString = function() { return "Primum"; };
Primum.ur = undefined;
}
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 = {};
actor.spawn = function(script, config){
if (typeof script !== 'string') return;
@ -19,21 +7,54 @@ actor.spawn = function(script, config){
if (typeof config === 'object')
Object.merge(padawan, config);
padawan.padawans = [];
padawan.timers = [];
padawan.master = this;
Object.hide(padawan, "master","timers");
this.padawans.push(padawan);
return padawan;
};
actor.die = function(actor){
};
actor.kill = function(actor){
this.timers.forEach(t => t());
this.timers = undefined;
actor.timers = [];
actor.kill = function(){
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) {
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);
return function() { t.kill(); };
};
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 = {
full_path() {
@ -148,7 +169,7 @@ var gameobject = {
set timescale(x) { cmd(168,this.body,x); },
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 velocity() { return q_body(3, this.body); },
set velocity(x) { set_body(9, this.body, x); },
@ -490,6 +511,7 @@ var gameobject = {
}
delete this.components;
// q_body(8,this.body);
this.clear();
@ -748,26 +770,12 @@ prototypes.from_file = function(file)
prototypes.list.push(urpath);
newur.toString = function() { return urpath; };
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;
}
prototypes.from_file.doc = "Create a new ur-type from a given script file.";
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()
{
var list = [];
@ -844,44 +852,6 @@ prototypes.generate_ur = function(path)
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)
{
if (!ur) return path;

View file

@ -677,9 +677,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 36:
id2go(js2int(argv[1]))->scale.XY = js2vec2(argv[2]);
gameobject_apply(id2go(js2int(argv[1])));
cpSpaceReindexShapesForBody(space, id2go(js2int(argv[1]))->body);
js2go(argv[1])->scale.XY = js2vec2(argv[2]);
gameobject_apply(js2go(argv[1]));
cpSpaceReindexShapesForBody(space, js2go(argv[1])->body);
break;
case 37:
@ -700,21 +700,21 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 40:
id2go(js2int(argv[1]))->filter.categories = js2bitmask(argv[2]);
gameobject_apply(id2go(js2int(argv[1])));
js2go(argv[1])->filter.categories = js2bitmask(argv[2]);
gameobject_apply(js2go(argv[1]));
break;
case 41:
id2go(js2int(argv[1]))->filter.mask = js2bitmask(argv[2]);
gameobject_apply(id2go(js2int(argv[1])));
js2go(argv[1])->filter.mask = js2bitmask(argv[2]);
gameobject_apply(js2go(argv[1]));
break;
case 42:
ret = bitmask2js(id2go(js2int(argv[1]))->filter.categories);
ret = bitmask2js(js2go(argv[1])->filter.categories);
break;
case 43:
ret = bitmask2js(id2go(js2int(argv[1]))->filter.mask);
ret = bitmask2js(js2go(argv[1])->filter.mask);
break;
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]);
break;
case 177:
plugin_node(js2ptr(argv[1]), js2ptr(argv[2]));
plugin_node(js2dsp_node(argv[1]), js2dsp_node(argv[2]));
break;
case 178:
ret = num2js(js2dsp_node(argv[1])->pan);
@ -1263,7 +1263,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 182:
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]);
break;
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));
case 8:
// gameobject_delete(goid);
gameobject_delete(go2id(go));
break;
}

View file

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

View file

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