Fix separation trigger; fix timers; add geometry creation; fix transform errors
This commit is contained in:
parent
563bacdb9c
commit
e712d06244
2
Makefile
2
Makefile
|
@ -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))
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -15,4 +16,7 @@ All objects in the Yugine can have an associated script. This script can perform
|
|||
|start|called when the object is loaded|
|
||||
|update(dt)|called once per game frame tick|
|
||||
|physupdate(dt)|called once per physics tick|
|
||||
|stop|called when the object is killed|
|
||||
|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|
|
|
@ -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,9 +382,42 @@ 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 */
|
||||
var collider2d = Object.copy(component, {
|
||||
name: "collider 2d",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -585,6 +628,8 @@ component.edge2d = Object.copy(collider2d, {
|
|||
knots = spoints.length + order
|
||||
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,26 +663,28 @@ 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;
|
||||
},
|
||||
|
||||
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 = [];
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
@ -834,13 +841,18 @@ prototypes.from_obj("camera2d", {
|
|||
z *= x;
|
||||
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);
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue