Fix separation trigger; fix timers; add geometry creation; fix transform errors

This commit is contained in:
John Alanbrook 2023-11-16 15:27:04 +00:00
parent 563bacdb9c
commit e712d06244
9 changed files with 195 additions and 67 deletions

View file

@ -174,7 +174,7 @@ tools/libcdb.a:
mv $(CDB)/libcdb.a tools
DOCOS = Sound gameobject Game Window physics Profile Time Player Mouse IO Log ColorMap sprite SpriteAnim Render
DOCOS = Sound gameobject Game Window physics Profile Time Player Mouse IO Log ColorMap sprite SpriteAnim Render Geometry
DOCHTML := $(addsuffix .api.html, $(DOCOS))
DOCMD := $(addsuffix .api.md, $(DOCOS))

View file

@ -5,7 +5,8 @@ Script hooks exist to allow to modification of the game.
|config.js|called before any game play, including play from editor|
|game.js|called to start the game|
|editorconfig.js|called when the editor is loaded, used to personalize|
|debug.js|called when play in editor is selected|
|predbg.js|called when play in editor is selected, before level load|
|debug.js|called when play in editor is selected, after level load|
|dbgret.js|called when play in editor returns to editor|
All objects in the Yugine can have an associated script. This script can perform setup, teardown, and handles responses for the object.
@ -16,3 +17,6 @@ All objects in the Yugine can have an associated script. This script can perform
|update(dt)|called once per game frame tick|
|physupdate(dt)|called once per physics tick|
|stop|called when the object is killed|
|debug|use draw functions with the object's world position|
|gui|draw functions in screen space|
|draw|draw functions in world space|

View file

@ -1,3 +1,16 @@
function make_point_obj(o, p)
{
return {
pos: p,
move(d) {
d = o.gameobject.dir_world2this(d);
p.x += d.x;
p.y += d.y;
},
sync: o.sync.bind(o)
}
}
function assign_impl(obj, impl)
{
var tmp = {};
@ -355,10 +368,6 @@ component.char2d.doc = {
add_anim: "Add an animation object with the given name."
};
// sprite.timer = timer.make(sprite.advance.bind(sprite),1);
// sprite.timer.loop = true;
/* Returns points specifying this geometry, with ccw */
var Geometry = {
box(w, h) {
@ -373,7 +382,40 @@ var Geometry = {
];
return points;
}
},
arc(radius, angle, n, start) {
start ??= 0;
start = Math.deg2rad(start);
if (angle >= 360)
angle = 360;
if (n <= 1) return [];
var points = [];
angle = Math.deg2rad(angle);
var arclen = angle/n;
for (var i = 0; i < n; i++)
points.push(Vector.rotate([radius,0], start + (arclen*i)));
return points;
},
circle(radius, n) {
if (n <= 1) return [];
return Geometry.arc(radius, 360, n);
},
ngon(radius, n) {
return Geometry.arc(radius,360,n);
},
};
Geometry.doc = {
doc: "Functions for creating a list of points for various geometric shapes.",
box: "Create a box.",
arc: "Create an arc, made of n points.",
circle: "Create a circle, made of n points."
};
/* For all colliders, "shape" is a pointer to a phys2d_shape, "id" is a pointer to the shape data */
@ -416,6 +458,10 @@ component.polygon2d = Object.copy(collider2d, {
hides: ['id', 'shape', 'gameobject'],
_enghook: make_poly2d,
points:[],
setpoints(points) {
this.points = points;
this.sync();
},
/* EDITOR */
get spoints() {
@ -454,16 +500,8 @@ component.polygon2d = Object.copy(collider2d, {
this.points = deep_copy(this.__proto__.points);
var p = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
if (p) {
return {
set pos(n) {
p.x = n.x;
p.y = n.y;
},
get pos() { return p; },
sync: this.sync.bind(this)
}
}
if (p)
return make_point_obj(this, p);
return undefined;
},
@ -560,6 +598,11 @@ component.edge2d = Object.copy(collider2d, {
return spoints;
},
setpoints(points) {
this.cpoints = points;
this.sync();
},
post() {
this.cpoints = [];
},
@ -575,7 +618,7 @@ component.edge2d = Object.copy(collider2d, {
return spoints;
if (spoints.length < 2)
return [];
if (this.samples === spoints.length) {
if (this.samples === 1) {
if (this.looped) return spoints.wrapped(1);
return spoints;
}
@ -586,6 +629,8 @@ component.edge2d = Object.copy(collider2d, {
assert knots%order != 0
*/
n = this.samples * this.sample_calc();
if (this.looped)
return Spline.sample(degrees, this.dimensions, Spline.type.open, spoints.wrapped(this.degrees), n);
@ -618,11 +663,8 @@ component.edge2d = Object.copy(collider2d, {
pick(pos) {
var p = Gizmos.pick_gameobject_points(pos, this.gameobject, this.cpoints);
if (p) return {
set pos(n) { p.x = n.x; p.y = n.y; },
get pos() { return p; },
sync: this.sync.bind(this),
};
if (p)
return make_point_obj(this, p);
return undefined;
},
@ -630,14 +672,19 @@ component.edge2d = Object.copy(collider2d, {
pick_all() {
var picks = [];
this.cpoints.forEach(function(x) {
picks.push({
set pos(n) { x.x = n.x; x.y = n.y; },
get pos() { return x; },
sync: this.sync.bind(this),
});
picks.push(make_point_obj(this,x));
}, this);
return picks;
},
sample_calc() {
return (this.spoints().length-1);
},
samples_per_cp() {
var s = this.sample_calc();
return this.samples/s;
},
});
component.edge2d.impl = Object.mix({
@ -646,7 +693,6 @@ component.edge2d.impl = Object.mix({
},
get thickness() { return cmd(112,this.id); },
sync() {
if (this.samples < this.spoints().length) this.samples = this.spoints().length;
var sensor = this.sensor;
var points = this.sample(this.samples);
cmd_edge2d(0,this.id,points);
@ -684,11 +730,16 @@ bucket.inputs['M-b'] = function() { this.thickness++; };
bucket.inputs['M-b'].doc = "Increase spline thickness.";
bucket.inputs['M-b'].rep = true;
bucket.inputs.plus = function() { this.samples++; };
bucket.inputs.plus = function() {
this.samples++;
};
bucket.inputs.plus.doc = "Increase the number of samples of this spline.";
bucket.inputs.plus.rep = true;
bucket.inputs.minus = function() { if (this.samples > this.spoints().length) this.samples--;};
bucket.inputs.minus = function() {
if (this.samples === 1) return;
this.samples--;
};
bucket.inputs.minus.doc = "Decrease the number of samples on this spline.";
bucket.inputs.minus.rep = true;
@ -698,25 +749,23 @@ bucket.inputs['C-r'].doc = "Reverse the order of the spline's points.";
bucket.inputs['C-l'] = function() { this.looped = !this.looped};
bucket.inputs['C-l'].doc = "Toggle spline being looped.";
bucket.inputs['C-c'] = function() { this.type = Spline.type.clamped; };
bucket.inputs['C-c'] = function() { this.type = Spline.type.clamped; this.looped = false; };
bucket.inputs['C-c'].doc = "Set type of spline to clamped.";
bucket.inputs['C-o'] = function() { this.type = Spline.type.open; };
bucket.inputs['C-o'] = function() { this.type = Spline.type.open; this.looped = false; };
bucket.inputs['C-o'].doc = "Set spline to open.";
bucket.inputs['C-b'] = function() { this.type = Spline.type.bezier; };
bucket.inputs['C-b'] = function() { this.type = Spline.type.bezier; this.looped = false;};
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(this.gameobject.world2this,this.gameobject), 25);
var idx = grab_from_points(Mouse.worldpos, this.cpoints.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return;
this.cpoints = this.cpoints.newfirst(idx);
};
bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
bucket.inputs.lm = function(){};
bucket.inputs['C-lm'] = function() {
var idx = 0;
@ -732,15 +781,14 @@ 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(screen2world(Mouse.pos), this.cpoints.map(function(x) {return x.sub(this.gameobject.worldpos()); }, this), 25);
var idx = cmd(59, Mouse.worldpos.sub(this.gameobject.pos), this.cpoints, 250);
bucket.inputs['S-lm'] = function() {
var idx = 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;
this.cpoints.splice(idx-1, 1);
this.cpoints.splice(idx, 1);
};
bucket.inputs['C-M-lm'].doc = "Remove point from the spline.";
bucket.inputs['S-lm'].doc = "Remove point from the spline.";
bucket.inputs.lb = function() {
var np = [];

View file

@ -210,6 +210,7 @@ var editor = {
Player.players[0].uncontrol(this);
Player.players[0].control(limited_editor);
Register.unregister_obj(this);
load("predbg.js");
editor.dbg_play = Primum.spawn(this.dbg_ur);
editor.dbg_play.pos = [0,0];
load("debug.js");
@ -596,7 +597,7 @@ var editor = {
var ur = prototypes.get_ur(file);
if (!ur) return;
var obj = editor.edit_level.spawn(ur);
obj.pos = Mouse.worldpos;
obj.set_worldpos(Mouse.worldpos);
this.selectlist = [obj];
},
@ -904,6 +905,8 @@ editor.inputs['C-s'] = function() {
Object.values(saveobj.objects).forEach(function(x) { x._ed.check_dirty(); });
Game.objects.forEach(function(x) {
if (typeof x !== 'object') return;
if (!('_ed' in x)) return;
if (x._ed.dirty) return;
x.revert();
x._ed.check_dirty();
@ -1153,11 +1156,11 @@ editor.inputs.mouse.move = function(pos, dpos)
if (editor.z_start)
editor.camera.zoom -= dpos.y/500;
else if (editor.joystart)
editor.camera.pos = editor.camera.pos.sub(dpos.scale(editor.camera.zoom));
editor.camera.pos = editor.camera.pos.sub(Game.camera.dir_view2world(dpos));
}
editor.grabselect?.forEach(function(x) {
x.pos = x.pos.add(dpos.scale(editor.camera.zoom));
x.move(Game.camera.dir_view2world(dpos));
if ('sync' in x)
x.sync();
});
@ -1184,12 +1187,8 @@ editor.inputs.mouse.move = function(pos, dpos)
editor.inputs.mouse.scroll = function(scroll)
{
scroll.y *= -1;
editor.grabselect?.forEach(function(x) {
x.pos = x.pos.add(scroll.scale(editor.camera.zoom));
});
scroll.y *= -1;
editor.camera.pos = editor.camera.pos.sub(scroll.scale(editor.camera.zoom * 3).scale([1,-1]));
// editor.grabselect?.forEach(x => x.move(Game.camera.dir_view2world(scroll)));
editor.camera.move(Game.camera.dir_view2world(scroll.scale(-3)));
}
editor.inputs.mouse['C-scroll'] = function(scroll)
@ -2000,7 +1999,7 @@ var entitylistpanel = Object.copy(inputpanel, {
this.level = editor.edit_level;
},
guibody() {
/* guibody() {
Nuke.newline(4);
Nuke.label("Object");
Nuke.label("Visible");
@ -2018,6 +2017,7 @@ var entitylistpanel = Object.copy(inputpanel, {
if (editor.selectlist.includes(x)) Nuke.label("T"); else Nuke.label("F");
});
},
*/
});
var limited_editor = {};

View file

@ -272,7 +272,7 @@ var Device = {
load("scripts/gui.js");
var timer = {
var ctimer = {
make(fn, secs,obj,loop,app) {
obj ??= globalThis;
app ??= false;
@ -308,6 +308,61 @@ var timer = {
get time() { return cmd(29, this.id); },
get pct() { return this.remain / this.time; },
};
var timer = {
time: 1,
remain: 1,
loop: false,
on: false,
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 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();
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);
Object.assign(t, desc);
t.time = secs;
t.remain = secs;
t.fn = fn;
Register.update.register(t.update, t);
return t;
},
};
timer.toJSON = function() { return undefined; };
timer.doc = {
doc: "Quickly make timers to fire off events once or multiple times.",

View file

@ -44,7 +44,7 @@ var gameobject = {
timers:[],
delay(fn, seconds) {
var t = timer.oneshot(fn, seconds, this, false);
var t = timer.oneshot(fn.bind(this), seconds, this, false);
this.timers.push(t);
return function() { t.kill(); };
},
@ -223,6 +223,9 @@ var gameobject = {
torque(val) { cmd(153, this.body, val); },
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body,pos); },
dir_world2this(dir) { return cmd(160, this.body, dir); },
dir_this2world(dir) { return cmd(161, this.body, dir); },
set layer(x) { cmd(75,this.body,x); },
get layer() { cmd(77,this.body); },
alive() { return this.body >= 0; },
@ -258,9 +261,13 @@ var gameobject = {
/* Make a unique object the same as its prototype */
revert() {
var jobj = this.json_obj();
var lobj = this.level.__proto__.objects[this.toString()];
delete jobj.objects;
Object.keys(jobj).forEach(function(x) {
this[x] = this.__proto__[x];
if (lobj && x in lobj)
this[x] = lobj[x];
else
this[x] = this.__proto__[x];
}, this);
this.sync();
},
@ -301,7 +308,7 @@ var gameobject = {
phys:Physics.static,
flipx() { return this.scale.x < 0; },
flipy() { return this.scale.y < 0; },
scale:[1,1,1],
scale:[1,1],
mirror(plane) {
this.scale = Vector.reflect(this.scale, plane);
},
@ -835,12 +842,17 @@ prototypes.from_obj("camera2d", {
cmd(62, z);
},
realzoom() {
return cmd(135);
},
speedmult: 1.0,
selectable: false,
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body,pos); },
dir_view2world(dir) {
return dir.scale(this.realzoom());
},
view2world(pos) {
return cmd(137,pos);

View file

@ -256,3 +256,8 @@ Cmdline.register_cmd("cjson", function(json) {
STD.exit(0);
}, "Clean up a jso file.");
Cmdline.register_cmd("r", function(script) {
run(script);
STD.exit(0);
}, "Run a script.");

View file

@ -587,9 +587,9 @@ void duk_call_phys_cb(HMM_Vec2 norm, struct callee c, int hit, cpArbiter *arb) {
HMM_Vec2 srfv;
srfv.cp = cpArbiterGetSurfaceVelocity(arb);
JS_SetPropertyStr(js, obj, "velocity", vec2js(srfv));
srfv.cp = cpArbiterGetPointA(arb,0);
JS_SetPropertyStr(js, obj, "pos", vec2js(srfv));
JS_SetPropertyStr(js,obj,"depth", num2js(cpArbiterGetDepth(arb,0)));
// srfv.cp = cpArbiterGetPointA(arb,0);
// JS_SetPropertyStr(js, obj, "pos", vec2js(srfv));
// JS_SetPropertyStr(js,obj,"depth", num2js(cpArbiterGetDepth(arb,0)));
JS_SetPropertyStr(js, obj, "id", JS_NewInt32(js,hit));
JS_SetPropertyStr(js,obj,"obj", JS_DupValue(js,id2go(hit)->ref));
@ -632,10 +632,8 @@ static cpBool handle_collision(cpArbiter *arb, int type) {
break;
case CTYPE_SEP:
if (JS_IsObject(go->cbs.separate.obj)) {
YughWarn("Made it here; separate.");
if (JS_IsObject(go->cbs.separate.obj))
duk_call_phys_cb(norm1, go->cbs.separate, g2, arb);
}
break;
}

View file

@ -1146,6 +1146,12 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 159:
ret = bool2js(js2go(argv[1])->gravity);
break;
case 160:
ret = vec2js(mat_t_dir(t_world2go(js2go(argv[1])), js2vec2(argv[2])));
break;
case 161:
ret = vec2js(mat_t_dir(t_go2world(js2go(argv[1])), js2vec2(argv[2])));
break;
}
if (str)