prosperon/scripts/components.js

820 lines
22 KiB
JavaScript
Raw Normal View History

var component = {
components: [],
toString() {
if ('gameobject' in this)
return this.name + " on " + this.gameobject;
else
return this.name;
},
name: "component",
component: true,
2023-05-27 07:01:17 -05:00
enabled: true,
enable() { this.enabled = true; },
disable() { this.enabled = false; },
2023-10-10 17:37:58 -05:00
2023-12-24 11:50:01 -06:00
isComponent(c) {
if (typeof c !== 'object') return false;
if (typeof c.toString !== 'function') return false;
if (typeof c.make !== 'function') return false;
return (typeof component[c.toString()] === 'object');
},
2023-10-10 17:37:58 -05:00
make(go) {
var nc = Object.create(this);
2023-12-19 15:34:36 -06:00
nc.gameobject = go;
Object.mixin(nc, this._enghook(go.body));
2023-10-10 17:37:58 -05:00
assign_impl(nc,this.impl);
2024-03-14 09:33:15 -05:00
Object.hide(nc, 'gameobject', 'id');
nc.post();
2023-10-10 17:37:58 -05:00
return nc;
},
2024-02-25 17:31:48 -06:00
kill() { console.info("Kill not created for this component yet"); },
sync(){},
post(){},
gui(){},
gizmo(){},
finish_center() {},
2023-12-19 15:34:36 -06:00
extend(spec) { return Object.copy(this, spec); },
};
var make_point_obj = function(o, p)
2024-02-25 17:31:48 -06:00
{
return {
pos: p,
move(d) {
d = o.gameobject.dir_world2this(d);
p.x += d.x;
p.y += d.y;
},
sync: o.sync.bind(o)
}
}
var assign_impl = function(obj, impl)
2024-02-25 17:31:48 -06:00
{
var tmp = {};
for (var key of Object.keys(impl))
if (typeof obj[key] !== 'undefined' && typeof obj[key] !== 'function')
tmp[key] = obj[key];
Object.mixin(obj, impl);
for (var key in tmp)
obj[key] = tmp[key];
}
function json_from_whitelist(whitelist)
{
return function() {
var o = {};
for (var p of whitelist)
o[p] = this[p];
return o;
}
}
2024-01-01 14:48:58 -06:00
2024-03-19 14:39:19 -05:00
Object.mixin(os.sprite(true), {
toJSON:json_from_whitelist([
"path",
"pos",
"scale",
"angle",
"color",
"emissive",
"parallax",
"frame"
]),
anim:{},
playing: 0,
play(str) {
var sp = this;
str ??= 0;
var playing = this.anim[str];
if (!playing) return; //TODO: ERROR
var f = 0;
function advance() {
sp.path = playing.path;
sp.frame = playing.frames[f].rect;
f = (f+1)%playing.frames.length;
if (f === 0)
sp.anim_done?.();
sp.ddd = sp.gameobject?.delay(advance, playing.frames[f].time);
2023-12-29 19:08:53 -06:00
}
advance();
},
set path(p) {
p = Resources.find_image(p);
if (!p) return;
if (p === this.path) return;
2024-03-18 08:16:25 -05:00
this.tex = texture.find(p);
var anim = SpriteAnim.make(p);
if (!anim) return;
this.anim = anim;
this.play();
2023-09-26 17:07:51 -05:00
},
get path() {
return this.tex.path();
2023-12-29 19:08:53 -06:00
},
kill() {
this.anim = undefined;
this.ddd?.();
delete this.ddd;
},
2023-10-10 17:37:58 -05:00
toString() { return "sprite"; },
move(d) { this.pos = this.pos.add(d); },
grow(x) {
this.scale = this.scale.scale(x);
this.pos = this.pos.scale(x);
},
2024-01-01 14:48:58 -06:00
sync() { },
pick() { return this; },
boundingbox() {
var dim = this.dimensions();
2023-12-20 17:20:29 -06:00
dim = dim.scale(this.gameobject.gscale());
var realpos = dim.scale(0.5).add(this.pos);
return bbox.fromcwh(realpos,dim);
},
2023-12-27 17:28:10 -06:00
dimensions() {
var dim = [this.tex.width(), this.tex.height()];
dim.x *= this.frame.w;
dim.y *= this.frame.h;
2023-12-27 17:28:10 -06:00
return dim;
},
width() { return this.dimensions().x; },
height() { return this.dimensions().y; },
});
2024-03-19 14:39:19 -05:00
os.sprite(true).make = function(go)
{
2024-03-19 14:39:19 -05:00
var sp = os.sprite();
sp.go = go.body;
sp.gameobject = go;
return sp;
}
2024-03-19 14:39:19 -05:00
component.sprite = os.sprite(true);
Object.freeze(sprite);
2023-10-04 17:57:37 -05:00
var sprite = component.sprite;
2023-09-20 13:33:11 -05:00
sprite.doc = {
path: "Path to the texture.",
color: "Color to mix with the sprite.",
pos: "The offset position of the sprite, relative to its entity."
};
sprite.inputs = {};
2023-12-20 17:20:29 -06:00
sprite.inputs.kp9 = function() { this.pos = this.dimensions().scale([0,0]); };
sprite.inputs.kp8 = function() { this.pos = this.dimensions().scale([-0.5, 0]); };
sprite.inputs.kp7 = function() { this.pos = this.dimensions().scale([-1,0]); };
sprite.inputs.kp6 = function() { this.pos = this.dimensions().scale([0,-0.5]); };
sprite.inputs.kp5 = function() { this.pos = this.dimensions().scale([-0.5,-0.5]); };
sprite.inputs.kp4 = function() { this.pos = this.dimensions().scale([-1,-0.5]); };
sprite.inputs.kp3 = function() { this.pos = this.dimensions().scale([0, -1]); };
sprite.inputs.kp2 = function() { this.pos = this.dimensions().scale([-0.5,-1]); };
sprite.inputs.kp1 = function() { this.pos = this.dimensions().scale([-1,-1]); };
Object.seal(sprite);
2023-12-29 19:08:53 -06:00
/* sprite anim returns a data structure for the given file path
frames: array of frames
rect: frame rectangle
time: miliseconds to hold the frame for
loop: true if it should be looped
*/
2023-11-01 15:33:22 -05:00
var SpriteAnim = {
2023-12-28 14:34:00 -06:00
make(path) {
if (path.ext() === 'gif')
return SpriteAnim.gif(path);
2024-01-01 14:48:58 -06:00
else if (path.ext() === 'ase')
return SpriteAnim.aseprite(path);
2023-12-29 19:08:53 -06:00
else
return undefined;
2023-12-28 14:34:00 -06:00
},
2023-11-01 15:33:22 -05:00
gif(path) {
var anim = {};
anim.frames = [];
anim.path = path;
2023-12-27 17:28:10 -06:00
var frames = Resources.gif.frames(path);
2023-11-01 15:33:22 -05:00
var yslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {
x: 0,
w: 1,
y: yslice*f,
h: yslice
2023-11-01 15:33:22 -05:00
};
frame.time = 0.05;
anim.frames.push(frame);
}
2024-03-19 14:39:19 -05:00
var times = render.gif_times(path);
2023-12-29 19:08:53 -06:00
for (var i = 0; i < frames; i++)
anim.frames[i].time = times[i]/1000;
2023-11-01 15:33:22 -05:00
anim.loop = true;
2023-12-27 17:28:10 -06:00
var dim = Resources.texture.dimensions(path);
2023-11-01 15:33:22 -05:00
dim.y /= frames;
anim.dim = dim;
2024-01-01 14:48:58 -06:00
return {0:anim};
2023-11-01 15:33:22 -05:00
},
2023-09-25 08:21:02 -05:00
strip(path, frames, time=0.05) {
2023-11-01 15:33:22 -05:00
var anim = {};
anim.frames = [];
anim.path = path;
var xslice = 1/frames;
for (var f = 0; f < frames; f++) {
var frame = {};
frame.rect = {s0:xslice*f, s1: xslice*(f+1), t0:0, t1:1};
frame.time = time;
2023-11-01 15:33:22 -05:00
anim.frames.push(frame);
}
2023-12-27 17:28:10 -06:00
anim.dim = Resources.texture.dimensions(path);
2023-11-01 15:33:22 -05:00
anim.dim.x /= frames;
return anim;
},
2023-09-25 08:21:02 -05:00
2023-11-01 15:33:22 -05:00
aseprite(path) {
function aseframeset2anim(frameset, meta) {
var anim = {};
anim.frames = [];
anim.path = meta.image;
var dim = meta.size;
var ase_make_frame = function(ase_frame,i) {
var f = ase_frame.frame;
var frame = {};
frame.rect = {
x: f.x/dim.w,
w: f.w/dim.w,
y: f.y/dim.h,
h: f.h/dim.h
2023-11-01 15:33:22 -05:00
};
frame.time = ase_frame.duration / 1000;
anim.frames.push(frame);
};
2023-09-25 08:21:02 -05:00
2023-11-01 15:33:22 -05:00
frameset.forEach(ase_make_frame);
anim.dim = [frameset[0].sourceSize.x, frameset[0].sourceSize.y];
anim.loop = true;
return anim;
};
2024-02-25 17:31:48 -06:00
var json = io.slurp(path);
2023-11-01 15:33:22 -05:00
json = JSON.parse(json);
var anims = {};
2024-01-01 14:48:58 -06:00
var frames = Array.isArray(json.frames) ? json.frames : Object.values(json.frames);
var f = 0;
for (var tag of json.meta.frameTags) {
2023-11-01 15:33:22 -05:00
anims[tag.name] = aseframeset2anim(frames.slice(tag.from, tag.to+1), json.meta);
2024-01-01 14:48:58 -06:00
anims[f] = anims[tag.name];
f++;
}
2023-11-01 15:33:22 -05:00
return anims;
},
validate(anim)
{
if (!Object.isObject(anim)) return false;
if (typeof anim.path !== 'string') return false;
if (typeof anim.dim !== 'object') return false;
return true;
},
find(path) {
2024-02-25 17:31:48 -06:00
if (!io.exists(path + ".asset")) return;
var asset = JSON.parse(io.slurp(path + ".asset"));
},
2023-11-01 15:33:22 -05:00
};
SpriteAnim.doc = 'Functions to create Primum animations from varying sources.';
SpriteAnim.gif.doc = 'Convert a gif.';
SpriteAnim.strip.doc = 'Given a path and number of frames, converts a horizontal strip animation, where each cell is the same width.'
SpriteAnim.aseprite.doc = 'Given an aseprite json metadata, returns an object of animations defined in the aseprite file.';
SpriteAnim.find.doc = 'Given a path, find the relevant animation for the file.';
/* 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",
2023-05-25 21:55:55 -05:00
sensor: false,
kill() {}, /* No killing is necessary - it is done through the gameobject's kill */
2023-10-11 17:22:41 -05:00
impl: {
2024-03-19 17:00:49 -05:00
set sensor(x) { pshape.set_sensor(this.shape,x); },
get sensor() { return pshape.get_sensor(this.shape); },
set enabled(x) { pshape.set_enabled(this.shape,x); },
get enabled() { return pshape.get_enabled(this.shape); }
2023-10-11 17:22:41 -05:00
},
});
Object.hide(collider2d.impl, 'enabled');
collider2d.inputs = {};
collider2d.inputs['M-s'] = function() { this.sensor = !this.sensor; }
collider2d.inputs['M-s'].doc = "Toggle if this collider is a sensor.";
collider2d.inputs['M-t'] = function() { this.enabled = !this.enabled; }
collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled.";
component.polygon2d = Object.copy(collider2d, {
toJSON:json_from_whitelist([
'points',
'sensor'
]),
2023-12-24 11:50:01 -06:00
toString() { return "polygon2d"; },
2023-10-10 17:37:58 -05:00
flipx: false,
flipy: false,
boundingbox() {
return bbox.frompoints(this.spoints());
},
2023-10-10 17:37:58 -05:00
hides: ['id', 'shape', 'gameobject'],
_enghook: make_poly2d,
2023-10-05 17:30:17 -05:00
points:[],
setpoints(points) {
this.points = points;
this.sync();
},
2023-10-05 17:30:17 -05:00
2023-05-27 07:01:17 -05:00
/* EDITOR */
2023-12-19 15:34:36 -06:00
spoints() {
var spoints = this.points.slice();
if (this.flipx) {
spoints.forEach(function(x) {
var newpoint = x.slice();
newpoint.x = -newpoint.x;
spoints.push(newpoint);
});
}
if (this.flipy) {
spoints.forEach(function(x) {
var newpoint = x.slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
});
}
return spoints;
},
gizmo() {
2024-02-25 17:31:48 -06:00
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.green));
2024-03-19 23:01:31 -05:00
this.points.forEach((x,i)=>render.coordinate(this.gameobject.this2screen(x), i));
},
pick(pos) {
2023-10-05 17:30:17 -05:00
if (!Object.hasOwn(this,'points'))
this.points = deep_copy(this.__proto__.points);
2023-12-18 06:45:27 -06:00
var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i];
if (p)
return make_point_obj(this, p);
2023-09-27 12:36:32 -05:00
return undefined;
},
2023-10-05 17:30:17 -05:00
});
function pointscaler(x) {
if (typeof x === 'number') return;
this.points = this.points.map(p => p.mult(x));
}
component.polygon2d.impl = Object.mix(collider2d.impl, {
2023-12-27 07:04:18 -06:00
sync() { cmd_poly2d(0, this.id, this.spoints());},
2024-03-19 14:39:19 -05:00
query() { return physics.shape_query(this.shape); },
grow: pointscaler,
});
var polygon2d = component.polygon2d;
polygon2d.inputs = {};
2023-12-27 07:04:18 -06:00
//polygon2d.inputs.post = function() { this.sync(); };
polygon2d.inputs.f10 = function() {
2024-02-25 17:31:48 -06:00
this.points = Math.sortpointsccw(this.points);
};
polygon2d.inputs.f10.doc = "Sort all points to be CCW order.";
polygon2d.inputs['C-lm'] = function() {
2024-03-11 22:23:02 -05:00
this.points.push(this.gameobject.world2this(Mouse.worldpos()));
};
polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse.";
polygon2d.inputs.lm = function(){};
polygon2d.inputs.lm.released = function(){};
polygon2d.inputs['C-M-lm'] = function() {
2024-03-11 22:23:02 -05:00
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);
};
polygon2d.inputs['C-M-lm'].doc = "Remove point under mouse.";
polygon2d.inputs['C-b'] = function() {
this.points = this.spoints;
this.flipx = false;
this.flipy = false;
};
polygon2d.inputs['C-b'].doc = "Freeze mirroring in place.";
component.edge2d = Object.copy(collider2d, {
toJSON:json_from_whitelist([
'sensor',
'thickness',
'points',
'hollow',
'hollowt',
]),
dimensions:2,
2023-10-05 17:30:17 -05:00
thickness:0,
2023-12-13 08:06:24 -06:00
/* if type === -1, point to point */
2023-12-12 08:46:27 -06:00
type: Spline.type.catmull,
2023-12-18 06:45:27 -06:00
C: 1, /* when in bezier, continuity required. 0, 1 or 2. */
looped: false,
2023-12-20 17:20:29 -06:00
angle: 0.5, /* smaller for smoother bezier */
flipx: false,
flipy: false,
points:[],
2023-10-05 17:30:17 -05:00
toString() { return "edge2d"; },
hollow: false,
2023-05-27 07:01:17 -05:00
hollowt: 0,
2023-10-12 17:05:49 -05:00
spoints() {
if (!this.points) return [];
var spoints = this.points.slice();
if (this.flipx) {
2023-12-20 17:20:29 -06:00
if (Spline.is_bezier(this.type))
spoints.push(Vector.reflect_point(spoints.at(-2), spoints.at(-1)));
for (var i = spoints.length-1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.x = -newpoint.x;
spoints.push(newpoint);
}
}
if (this.flipy) {
2023-12-20 17:20:29 -06:00
if (Spline.is_bezier(this.type))
spoints.push(Vector.reflect(point(spoints.at(-2),spoints.at(-1))));
for (var i = spoints.length-1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
}
}
if (this.hollow) {
var hpoints = inflate_cpv(spoints, spoints.length, this.hollowt);
if (hpoints.length === spoints.length) return spoints;
var arr1 = hpoints.filter(function(x,i) { return i % 2 === 0; });
var arr2 = hpoints.filter(function(x,i) { return i % 2 !== 0; });
return arr1.concat(arr2.reverse());
}
return spoints;
},
setpoints(points) {
this.points = points;
2023-12-27 07:04:18 -06:00
// this.sync();
},
post() {
this.points = [];
},
2023-12-13 08:06:24 -06:00
sample() {
2023-10-12 17:05:49 -05:00
var spoints = this.spoints();
2024-03-09 18:22:06 -06:00
if (spoints.length === 0) return [];
2023-12-13 08:06:24 -06:00
if (this.type === -1) {
if (this.looped) spoints.push(spoints[0]);
return spoints;
}
2023-12-13 19:53:09 -06:00
if (this.type === Spline.type.catmull) {
2023-12-18 06:45:27 -06:00
if (this.looped)
spoints = Spline.catmull_loop(spoints);
else
spoints = Spline.catmull_caps(spoints);
2023-12-13 19:53:09 -06:00
return Spline.sample_angle(this.type, spoints,this.angle);
2023-12-12 19:35:34 -06:00
}
2023-12-13 19:53:09 -06:00
2023-12-20 17:20:29 -06:00
if (this.looped && Spline.is_bezier(this.type))
spoints = Spline.bezier_loop(spoints);
2023-12-12 08:46:27 -06:00
return Spline.sample_angle(this.type, spoints, this.angle);
},
boundingbox() { return bbox.frompoints(this.points.map(x => x.scale(this.gameobject.scale))); },
hides: ['gameobject', 'id', 'shape'],
2023-10-11 17:22:41 -05:00
_enghook: make_edge2d,
2023-05-27 07:01:17 -05:00
/* EDITOR */
gizmo() {
2023-12-27 10:34:14 -06:00
if (this.type === Spline.type.catmull || this.type === -1) {
2024-02-25 17:31:48 -06:00
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.teal));
2024-03-19 23:01:31 -05:00
this.points.forEach((x,i) => render.coordinate(this.gameobject.this2screen(x), i));
2023-12-15 12:45:09 -06:00
} else {
for (var i = 0; i < this.points.length; i += 3)
2024-03-19 23:01:31 -05:00
render.coordinate(this.gameobject.this2screen(this.points[i]), i, Color.teal);
2023-12-18 06:45:27 -06:00
for (var i = 1; i < this.points.length; i+=3) {
2024-03-19 23:01:31 -05:00
render.coordinate(this.gameobject.this2screen(this.points[i]), i, Color.green);
render.coordinate(this.gameobject.this2screen(this.points[i+1]), i+1, Color.green);
render.line([this.gameobject.this2screen(this.points[i-1]), this.gameobject.this2screen(this.points[i])], Color.yellow);
render.line([this.gameobject.this2screen(this.points[i+1]), this.gameobject.this2screen(this.points[i+2])], Color.yellow);
2023-12-15 12:45:09 -06:00
}
}
},
2023-05-27 07:01:17 -05:00
finish_center(change) { this.points = this.points.map(function(x) { return x.sub(change); }); },
2023-05-27 07:01:17 -05:00
pick(pos) {
var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i];
2023-12-18 17:12:05 -06:00
if (!p) return undefined;
2023-12-27 10:34:14 -06:00
if (Spline.is_catmull(this.type) || this.type === -1)
2023-12-18 17:12:05 -06:00
return make_point_obj(this,p);
2023-12-18 06:45:27 -06:00
var that = this.gameobject;
var me = this;
if (p) {
var o = {
pos: p,
sync: me.sync.bind(me)
};
if (Spline.bezier_is_handle(this.points,i))
2023-12-18 06:45:27 -06:00
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
Spline.bezier_cp_mirror(me.points,i);
2023-12-18 06:45:27 -06:00
};
else
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
var pp = Spline.bezier_point_handles(me.points,i);
pp.forEach(ph => me.points[ph] = me.points[ph].add(d));
2023-12-18 06:45:27 -06:00
}
return o;
}
},
2023-12-19 15:34:36 -06:00
rm_node(idx) {
if (idx < 0 || idx >= this.points.length) return;
2023-12-19 15:34:36 -06:00
if (Spline.is_catmull(this.type))
this.points.splice(idx,1);
2023-12-19 15:34:36 -06:00
if (Spline.is_bezier(this.type)) {
assert(Spline.bezier_is_node(this.points, idx), 'Attempted to delete a bezier handle.');
2023-12-19 15:34:36 -06:00
if (idx === 0)
this.points.splice(idx,2);
else if (idx === this.points.length-1)
this.points.splice(this.points.length-2,2);
2023-12-19 15:34:36 -06:00
else
this.points.splice(idx-1,3);
2023-12-19 15:34:36 -06:00
}
},
add_node(pos) {
pos = this.gameobject.world2this(pos);
var idx = 0;
2023-12-27 10:34:14 -06:00
if (Spline.is_catmull(this.type) || this.type === -1) {
if (this.points.length >= 2)
2024-03-19 14:39:19 -05:00
idx = physics.closest_point(pos, this.points, 400);
2023-12-19 15:34:36 -06:00
if (idx === this.points.length)
this.points.push(pos);
2023-12-19 15:34:36 -06:00
else
this.points.splice(idx, 0, pos);
2023-12-19 15:34:36 -06:00
}
if (Spline.is_bezier(this.type)) {
2024-03-19 14:39:19 -05:00
idx = physics.closest_point(pos, Spline.bezier_nodes(this.points),400);
2023-12-21 10:49:44 -06:00
2023-12-19 15:34:36 -06:00
if (idx < 0) return;
2023-12-21 10:49:44 -06:00
if (idx === 0) {
this.points.unshift(pos.slice(), pos.add([-100,0]), Vector.reflect_point(this.points[1], this.points[0]));
2023-12-21 10:49:44 -06:00
return;
}
if (idx === Spline.bezier_node_count(this.points)) {
this.points.push(Vector.reflect_point(this.points.at(-2), this.points.at(-1)), pos.add([-100,0]), pos.slice());
2023-12-21 10:49:44 -06:00
return;
}
idx = 2 + (idx-1)*3;
var adds = [pos.add([100,0]), pos.slice(), pos.add([-100,0])];
this.points.splice(idx, 0, ...adds);
2023-12-19 15:34:36 -06:00
}
},
pick_all() {
var picks = [];
this.points.forEach(x =>picks.push(make_point_obj(this,x)));
return picks;
},
});
component.edge2d.impl = Object.mix(collider2d.impl, {
2023-10-05 17:30:17 -05:00
set thickness(x) {
cmd_edge2d(1,this.id,x);
},
2024-03-19 14:39:19 -05:00
get thickness() { return physics.edge_thickness(this.id); },
grow: pointscaler,
2023-10-05 17:30:17 -05:00
sync() {
var sensor = this.sensor;
2023-12-13 08:06:24 -06:00
var points = this.sample();
2023-12-12 19:35:34 -06:00
if (!points) return;
2023-10-11 17:22:41 -05:00
cmd_edge2d(0,this.id,points);
2023-10-05 17:30:17 -05:00
this.sensor = sensor;
},
});
2023-10-05 17:30:17 -05:00
var bucket = component.edge2d;
bucket.spoints.doc = "Returns the controls points after modifiers are applied, such as it being hollow or mirrored on its axises.";
bucket.inputs = {};
2023-12-27 07:04:18 -06:00
//bucket.inputs.post = function() { this.sync(); };
bucket.inputs.h = function() { this.hollow = !this.hollow; };
bucket.inputs.h.doc = "Toggle hollow.";
bucket.inputs['C-g'] = function() { if (this.hollowt > 0) this.hollowt--; };
bucket.inputs['C-g'].doc = "Thin the hollow thickness.";
bucket.inputs['C-g'].rep = true;
bucket.inputs['C-f'] = function() { this.hollowt++; };
bucket.inputs['C-f'].doc = "Increase the hollow thickness.";
bucket.inputs['C-f'].rep = true;
bucket.inputs['M-v'] = function() { if (this.thickness > 0) this.thickness--; };
bucket.inputs['M-v'].doc = "Decrease spline thickness.";
bucket.inputs['M-v'].rep = true;
bucket.inputs['C-y'] = function() {
this.points = this.spoints();
this.flipx = false;
this.flipy = false;
this.hollow = false;
};
bucket.inputs['C-y'].doc = "Freeze mirroring,";
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() {
2023-12-13 08:06:24 -06:00
if (this.angle <= 1) {
this.angle = 1;
return;
}
this.angle *= 0.9;
};
bucket.inputs.plus.doc = "Increase the number of samples of this spline.";
bucket.inputs.plus.rep = true;
2023-12-13 08:06:24 -06:00
bucket.inputs.minus = function() { this.angle *= 1.1; };
bucket.inputs.minus.doc = "Decrease the number of samples on this spline.";
bucket.inputs.minus.rep = true;
bucket.inputs['C-r'] = function() { this.points = this.points.reverse(); };
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.";
2023-12-18 06:45:27 -06:00
bucket.inputs['C-c'] = function() {
switch(this.type) {
case Spline.type.bezier:
this.points = Spline.bezier2catmull(this.points);
2023-12-18 06:45:27 -06:00
break;
}
this.type = Spline.type.catmull;
};
2023-12-13 08:06:24 -06:00
bucket.inputs['C-c'].doc = "Set type of spline to catmull-rom.";
2023-12-18 06:45:27 -06:00
bucket.inputs['C-b'] = function() {
switch(this.type) {
case Spline.type.catmull:
this.points = Spline.catmull2bezier(Spline.catmull_caps(this.points));
2023-12-18 06:45:27 -06:00
break;
}
this.type = Spline.type.bezier;
};
2023-12-13 19:53:09 -06:00
2023-12-13 08:06:24 -06:00
bucket.inputs['C-o'] = function() { this.type = -1; };
bucket.inputs['C-o'].doc = "Set spline to linear.";
bucket.inputs['C-M-lm'] = function() {
2023-12-19 15:34:36 -06:00
if (Spline.is_catmull(this.type)) {
2024-03-11 22:23:02 -05:00
var idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
2023-12-19 15:34:36 -06:00
if (idx === -1) return;
} else {
}
this.points = this.points.newfirst(idx);
};
bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
2024-03-11 22:23:02 -05:00
bucket.inputs['C-lm'] = function() { this.add_node(Mouse.worldpos()); }
bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
bucket.inputs['C-M-lm'] = function() {
2023-12-19 15:34:36 -06:00
var idx = -1;
if (Spline.is_catmull(this.type))
2024-03-11 22:23:02 -05:00
idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
2023-12-19 15:34:36 -06:00
else {
var nodes = Spline.bezier_nodes(this.points);
2024-03-11 22:23:02 -05:00
idx = Math.grab_from_points(Mouse.worldpos(), nodes.map(p => this.gameobject.this2world(p)), 25);
2023-12-19 15:34:36 -06:00
idx *= 3;
}
2023-12-19 15:34:36 -06:00
this.rm_node(idx);
};
bucket.inputs['C-M-lm'].doc = "Remove point from the spline.";
bucket.inputs.lm = function(){};
bucket.inputs.lm.released = function(){};
bucket.inputs.lb = function() {
var np = [];
this.points.forEach(function(c) {
np.push(Vector.rotate(c, Math.deg2rad(-1)));
});
this.points = np;
};
bucket.inputs.lb.doc = "Rotate the points CCW.";
bucket.inputs.lb.rep = true;
bucket.inputs.rb = function() {
var np = [];
this.points.forEach(function(c) {
np.push(Vector.rotate(c, Math.deg2rad(1)));
});
this.points = np;
};
bucket.inputs.rb.doc = "Rotate the points CW.";
bucket.inputs.rb.rep = true;
component.circle2d = Object.copy(collider2d, {
2023-10-04 17:57:37 -05:00
radius:10,
offset:[0,0],
toString() { return "circle2d"; },
2023-09-20 13:33:11 -05:00
boundingbox() {
return bbox.fromcwh(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]);
2023-09-20 13:33:11 -05:00
},
2023-10-10 17:37:58 -05:00
hides: ['gameobject', 'id', 'shape', 'scale'],
_enghook: make_circle2d,
});
2023-05-01 20:58:10 -05:00
component.circle2d.impl = Object.mix({
toJSON:json_from_whitelist([
"pos",
"radius",
]),
2023-10-11 17:22:41 -05:00
set radius(x) { cmd_circle2d(0,this.id,x); },
get radius() { return cmd_circle2d(2,this.id); },
set scale(x) { this.radius = x; },
get scale() { return this.radius; },
set offset(x) { cmd_circle2d(1,this.id,x); },
get offset() { return cmd_circle2d(3,this.id); },
2023-12-21 10:49:44 -06:00
2024-03-01 11:45:06 -06:00
get pos() { return cmd_circle2d(3,this.id); },
set pos(x) { cmd_circle2d(1,this.id,x); },
grow(x) {
if (typeof x === 'number') this.scale *= x;
else if (typeof x === 'object') this.scale *= x[0];
},
2023-12-21 10:49:44 -06:00
2023-12-29 19:08:53 -06:00
}, collider2d.impl);
return {component, SpriteAnim};