changed make to spawn; unified actor system for entities
This commit is contained in:
parent
de74b42be2
commit
1e432346ff
|
@ -119,7 +119,6 @@ When an actor dies, all of the actors that have it as their master[fn::What a mo
|
||||||
*** Turns
|
*** Turns
|
||||||
Actors get fragments of time called a *turn*. Actors which belong to different systems can have different lengths of turns.
|
Actors get fragments of time called a *turn*. Actors which belong to different systems can have different lengths of turns.
|
||||||
|
|
||||||
|
|
||||||
*** Actor files
|
*** Actor files
|
||||||
Actor files end with the extension *.jso*[fn::"Javascript object".]. They list a series of functions to call on a newly formed actor. Actors have a number of useful functions which are called as defined.
|
Actor files end with the extension *.jso*[fn::"Javascript object".]. They list a series of functions to call on a newly formed actor. Actors have a number of useful functions which are called as defined.
|
||||||
|
|
||||||
|
@ -184,7 +183,11 @@ Game worlds are made of entities. Entities are a type of actor with a number of
|
||||||
The first and most masterful entity is the Primum. The Primum has no components, and its rotation and position are zero. It defines the center of the game.
|
The first and most masterful entity is the Primum. The Primum has no components, and its rotation and position are zero. It defines the center of the game.
|
||||||
#+end_scholium
|
#+end_scholium
|
||||||
|
|
||||||
In editor mode, when an entity moves, all of its *padawans* also move. When the game is actively simulating, this only holds if there are physical constraints between them.
|
In editor mode, when an entity moves, all of its *padawans* also move.
|
||||||
|
|
||||||
|
When the game is actively simulating, this only holds if there are physical constraints between them.
|
||||||
|
|
||||||
|
Prosperon automatically generates physical pin constraints between objects with the appropriate physical properties.
|
||||||
|
|
||||||
*** Adding Components
|
*** Adding Components
|
||||||
Entities can have *components*. Components are essentially javascript wrappers over C code into the engine. Scripting is done to set the components up on entities, after which most of the work is done by the C plugin.
|
Entities can have *components*. Components are essentially javascript wrappers over C code into the engine. Scripting is done to set the components up on entities, after which most of the work is done by the C plugin.
|
||||||
|
@ -206,7 +209,7 @@ Components only work in the context of an entity. They have no meaning outside o
|
||||||
While components can be added via scripting, it is easier to add them via the editor, as we will later see.
|
While components can be added via scripting, it is easier to add them via the editor, as we will later see.
|
||||||
|
|
||||||
*** Ur system
|
*** Ur system
|
||||||
The ur[fn::A German prefix meaning primitive, original, or earliest.] system is a prototypical inheritence system used by the actor files. When actor files are loaded, they are stored as an ur. Entities can be created from ur types using the *spawn* function.
|
The ur[fn::A German prefix meaning primitive, original, or earliest.] system is a prototypical inheritence system used by the actor files. When actor files are loaded, they are stored as an ur. An *ur* represents a set of instructions to create the (text, config) needed to spawn an actor or entity.
|
||||||
|
|
||||||
#+begin_scholium
|
#+begin_scholium
|
||||||
Create an ur from the *hello* files above, and then spawn it.
|
Create an ur from the *hello* files above, and then spawn it.
|
||||||
|
@ -217,11 +220,21 @@ Primum.spawn(ur.hello);
|
||||||
When creating an actor from source files, all of its setup must take place. In this example, the setup happens during *ur.create*, and spawning is simply a matter of prototyping it.
|
When creating an actor from source files, all of its setup must take place. In this example, the setup happens during *ur.create*, and spawning is simply a matter of prototyping it.
|
||||||
#+end_scholium
|
#+end_scholium
|
||||||
|
|
||||||
Each ur type has some useful fields.
|
Each ur has the following fields.
|
||||||
|
|
||||||
| field | description |
|
| field | description |
|
||||||
|-----------+----------------------------------|
|
|-----------+-------------------------------------------------------------|
|
||||||
| instances | An array of instances of this ur |
|
| instances | An array of instances of this ur |
|
||||||
|
| name | Name of the ur |
|
||||||
|
| text | Path to the script file |
|
||||||
|
| data | Object to write to a newly generated actor |
|
||||||
|
| proto | An object that looks like a freshly made entity from the ur |
|
||||||
|
|
||||||
|
An *ur* has a full path given like ~ur.goblin.big~. ~goblin~ and ~big~ can both possibly have a *.jso* script as well as a *data* file.
|
||||||
|
|
||||||
|
When ~goblin.big~ is created, the new object has the ~goblin~ script run on it, followed by the ~big~ script. The ~data~ fields consist of objects prototyped from each other, so that the ~__proto__~ of ~big.data~ is ~goblin.data~. All fields of this objects are assigned to the ~big goblin~.
|
||||||
|
|
||||||
|
The unaltered form of every ur-based-entity is saved in the ur's ~proto~ field. As you edit objects, the differences between how your object is now, compared to its ~ur.proto~ is a list of differences. These differences can be rolled into the ~ur~, or saved as a subtype.
|
||||||
|
|
||||||
*** Prototyping Entities
|
*** Prototyping Entities
|
||||||
Ur types are the prototype of created entities. This makes it trivial to change huge swathes of the game, or make tiny adjustments to single objects, in a natural and intuitive way. When a value is changed on an entity, it is private. When a value is changed on an ur, it propogates to all entities. Values cannot be added or removed in subtypes.
|
Ur types are the prototype of created entities. This makes it trivial to change huge swathes of the game, or make tiny adjustments to single objects, in a natural and intuitive way. When a value is changed on an entity, it is private. When a value is changed on an ur, it propogates to all entities. Values cannot be added or removed in subtypes.
|
||||||
|
|
72
scripts/actor.js
Normal file
72
scripts/actor.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
var actor = {};
|
||||||
|
var a_db = {};
|
||||||
|
|
||||||
|
actor.spawn = function(script, config){
|
||||||
|
if (typeof script !== 'string') return undefined;
|
||||||
|
if (!a_db[script]) a_db[script] = io.slurp(script);
|
||||||
|
var padawan = Object.create(actor);
|
||||||
|
eval_env(a_db[script], padawan);
|
||||||
|
|
||||||
|
if (typeof config === 'object')
|
||||||
|
Object.merge(padawan,config);
|
||||||
|
|
||||||
|
padawan.padawans = [];
|
||||||
|
padawan.timers = [];
|
||||||
|
padawan.master = this;
|
||||||
|
Object.hide(padawan, "master","timers", "padawans");
|
||||||
|
this.padawans.push(padawan);
|
||||||
|
return padawan;
|
||||||
|
};
|
||||||
|
|
||||||
|
actor.spawn.doc = `Create a new actor, using this actor as the master, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`;
|
||||||
|
|
||||||
|
actor.timers = [];
|
||||||
|
actor.kill = function(){
|
||||||
|
if (this.__dead__) return;
|
||||||
|
this.timers.forEach(t => t.kill());
|
||||||
|
if (this.master)
|
||||||
|
delete this.master[this.toString()];
|
||||||
|
this.padawans.forEach(p => p.kill());
|
||||||
|
this.padawans = [];
|
||||||
|
this.__dead__ = true;
|
||||||
|
if (typeof this.die === 'function') this.die();
|
||||||
|
if (typeof this.stop === 'function') this.stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
actor.kill.doc = `Remove this actor and all its padawans from existence.`;
|
||||||
|
|
||||||
|
actor.delay = function(fn, seconds) {
|
||||||
|
var t = Object.create(timer);
|
||||||
|
t.remain = seconds;
|
||||||
|
t.kill = () => {
|
||||||
|
timer.kill.call(t);
|
||||||
|
delete this.timers[t.toString()];
|
||||||
|
}
|
||||||
|
t.fire = () => {
|
||||||
|
if (this.__dead__) return;
|
||||||
|
fn();
|
||||||
|
t.kill();
|
||||||
|
};
|
||||||
|
Register.appupdate.register(t.update, t);
|
||||||
|
this.timers.push(t);
|
||||||
|
return function() { t.kill(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
|
||||||
|
|
||||||
|
actor.padawans = [];
|
||||||
|
|
||||||
|
actor.remaster = function(to){
|
||||||
|
delete this.master.padawans[this.toString()];
|
||||||
|
this.master = to;
|
||||||
|
to.padawans.push(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
global.app = Object.create(actor);
|
||||||
|
|
||||||
|
app.die = function()
|
||||||
|
{
|
||||||
|
Game.quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {actor, app};
|
|
@ -38,7 +38,7 @@ var ai = {
|
||||||
if (Vector.length(dir) < 10) return true;
|
if (Vector.length(dir) < 10) return true;
|
||||||
|
|
||||||
this.velocity = Vector.norm(this.randomloc.sub(this.pos)).scale(20);
|
this.velocity = Vector.norm(this.randomloc.sub(this.pos)).scale(20);
|
||||||
return false;
|
return False;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -61,3 +61,5 @@ var ai = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return {ai};
|
||||||
|
|
|
@ -465,6 +465,18 @@ Object.containingKey = function(obj, prop)
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object.access = function(obj, name)
|
||||||
|
{
|
||||||
|
var dig = name.split('.');
|
||||||
|
|
||||||
|
for (var i of dig) {
|
||||||
|
obj = obj[i];
|
||||||
|
if (!obj) return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
Object.isAccessor = function(obj, prop)
|
Object.isAccessor = function(obj, prop)
|
||||||
{
|
{
|
||||||
var o = Object.containingKey(obj,prop);
|
var o = Object.containingKey(obj,prop);
|
||||||
|
@ -490,11 +502,11 @@ Object.mergekey = function(o1,o2,k)
|
||||||
o1[k] = o2[k];
|
o1[k] = o2[k];
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
Object.defineProperty(o1, k, Object.getOwnPropertyDescriptor(o2,k));
|
o1[k] = o2[k];
|
||||||
// o1[k] = o2[k];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Same as merge from Ruby */
|
/* Same as merge from Ruby */
|
||||||
|
/* Adds objs key by key to target */
|
||||||
Object.merge = function(target, ...objs)
|
Object.merge = function(target, ...objs)
|
||||||
{
|
{
|
||||||
for (var obj of objs)
|
for (var obj of objs)
|
||||||
|
@ -575,10 +587,12 @@ Object.defineProperty(Object.prototype, 'obscure', {
|
||||||
Object.defineProperty(Object.prototype, 'mixin', {
|
Object.defineProperty(Object.prototype, 'mixin', {
|
||||||
value: function(obj) {
|
value: function(obj) {
|
||||||
if (typeof obj === 'string') {
|
if (typeof obj === 'string') {
|
||||||
obj = use(obj);
|
var script = io.slurp(obj);
|
||||||
if (!obj) return;
|
obj = eval_env(script, this, obj);
|
||||||
}
|
}
|
||||||
Object.assign(this, obj);
|
|
||||||
|
if (obj)
|
||||||
|
Object.mixin(this, obj);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -771,6 +785,28 @@ Object.defineProperty(String.prototype, 'set_ext', {
|
||||||
value: function(val) { return this.strip_ext() + val; }
|
value: function(val) { return this.strip_ext() + val; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(String.prototype, 'folder_same_name', {
|
||||||
|
value: function() {
|
||||||
|
var dirs = this.dir().split('/');
|
||||||
|
return dirs.last() === this.name();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(String.prototype, 'up_path', {
|
||||||
|
value: function() {
|
||||||
|
var base = this.base();
|
||||||
|
var dirs = this.dir().split('/');
|
||||||
|
dirs.pop();
|
||||||
|
return dirs.join('/') + base;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(String.prototype, 'resolve', {
|
||||||
|
value: function(path) {
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(String.prototype, 'fromlast', {
|
Object.defineProperty(String.prototype, 'fromlast', {
|
||||||
value: function(val) {
|
value: function(val) {
|
||||||
var idx = this.lastIndexOf(val);
|
var idx = this.lastIndexOf(val);
|
||||||
|
@ -816,7 +852,10 @@ Object.defineProperty(String.prototype, 'base', {
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(String.prototype, 'dir', {
|
Object.defineProperty(String.prototype, 'dir', {
|
||||||
value: function() { return this.tolast('/'); }
|
value: function() {
|
||||||
|
if (!this.includes('/')) return "";
|
||||||
|
return this.tolast('/');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(String.prototype, 'splice', {
|
Object.defineProperty(String.prototype, 'splice', {
|
||||||
|
|
19
scripts/camera2d.jso
Normal file
19
scripts/camera2d.jso
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
this.phys = physics.kinematic;
|
||||||
|
this.dir_view2world = function(dir) { return dir.scale(this.realzoom()); };
|
||||||
|
this.view2world = function(pos) { return cmd(137,pos); };
|
||||||
|
this.world2view = function(pos) { return cmd(136,pos); };
|
||||||
|
this.realzoom = function() { return cmd(135); };
|
||||||
|
|
||||||
|
this.mixin({
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
});
|
|
@ -42,8 +42,7 @@ var component = {
|
||||||
extend(spec) { return Object.copy(this, spec); },
|
extend(spec) { return Object.copy(this, spec); },
|
||||||
};
|
};
|
||||||
|
|
||||||
component.util = {};
|
var make_point_obj = function(o, p)
|
||||||
component.util.make_point_obj = function(o, p)
|
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
pos: p,
|
pos: p,
|
||||||
|
@ -56,7 +55,7 @@ component.util.make_point_obj = function(o, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component.util.assign_impl = function(obj, impl)
|
var assign_impl = function(obj, impl)
|
||||||
{
|
{
|
||||||
var tmp = {};
|
var tmp = {};
|
||||||
for (var key of Object.keys(impl))
|
for (var key of Object.keys(impl))
|
||||||
|
@ -172,7 +171,7 @@ component.sprite.impl = {
|
||||||
var dim = this.dimensions();
|
var dim = this.dimensions();
|
||||||
dim = dim.scale(this.gameobject.gscale());
|
dim = dim.scale(this.gameobject.gscale());
|
||||||
var realpos = dim.scale(0.5).add(this.pos);
|
var realpos = dim.scale(0.5).add(this.pos);
|
||||||
return cwh2bb(realpos,dim);
|
return bbox.fromcwh(realpos,dim);
|
||||||
},
|
},
|
||||||
|
|
||||||
kill() { cmd(9,this.id); },
|
kill() { cmd(9,this.id); },
|
||||||
|
@ -801,7 +800,7 @@ component.circle2d = Object.copy(collider2d, {
|
||||||
toString() { return "circle2d"; },
|
toString() { return "circle2d"; },
|
||||||
|
|
||||||
boundingbox() {
|
boundingbox() {
|
||||||
return cwh2bb(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]);
|
return bbox.fromcwh(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]);
|
||||||
},
|
},
|
||||||
|
|
||||||
hides: ['gameobject', 'id', 'shape', 'scale'],
|
hides: ['gameobject', 'id', 'shape', 'scale'],
|
||||||
|
|
|
@ -1,77 +1,3 @@
|
||||||
/* All draw in screen space */
|
|
||||||
Object.assign(render, {
|
|
||||||
point(pos,size,color) {
|
|
||||||
color ??= Color.blue;
|
|
||||||
render.circle(pos,size,color);
|
|
||||||
},
|
|
||||||
|
|
||||||
line(points, color, thickness) {
|
|
||||||
thickness ??= 1;
|
|
||||||
color ??= Color.white;
|
|
||||||
cmd(83, points, color, thickness);
|
|
||||||
},
|
|
||||||
|
|
||||||
poly(points, color) { cmd_points(0,points,color); },
|
|
||||||
|
|
||||||
circle(pos, radius, color) { cmd(115, pos, radius, color); },
|
|
||||||
|
|
||||||
/* size here is arm length - size of 2 is 4 height total */
|
|
||||||
cross(pos, size, color) {
|
|
||||||
color ??= Color.red;
|
|
||||||
var a = [
|
|
||||||
pos.add([0,size]),
|
|
||||||
pos.add([0,-size])
|
|
||||||
];
|
|
||||||
var b = [
|
|
||||||
pos.add([size,0]),
|
|
||||||
pos.add([-size,0])
|
|
||||||
];
|
|
||||||
|
|
||||||
render.line(a,color);
|
|
||||||
render.line(b,color);
|
|
||||||
},
|
|
||||||
|
|
||||||
arrow(start, end, color, wingspan, wingangle) {
|
|
||||||
color ??= Color.red;
|
|
||||||
wingspan ??= 4;
|
|
||||||
wingangle ??=10;
|
|
||||||
|
|
||||||
var dir = end.sub(start).normalized();
|
|
||||||
var wing1 = [
|
|
||||||
Vector.rotate(dir, wingangle).scale(wingspan).add(end),
|
|
||||||
end
|
|
||||||
];
|
|
||||||
var wing2 = [
|
|
||||||
Vector.rotate(dir,-wingangle).scale(wingspan).add(end),
|
|
||||||
end
|
|
||||||
];
|
|
||||||
render.line([start,end],color);
|
|
||||||
render.line(wing1,color);
|
|
||||||
render.line(wing2,color);
|
|
||||||
},
|
|
||||||
|
|
||||||
rectangle(lowerleft, upperright, color) {
|
|
||||||
var pos = lowerleft.add(upperright).map(x=>x/2);
|
|
||||||
var wh = [upperright.x-lowerleft.x,upperright.y-lowerleft.y];
|
|
||||||
render.box(pos,wh,color);
|
|
||||||
},
|
|
||||||
|
|
||||||
box(pos, wh, color) {
|
|
||||||
color ??= Color.white;
|
|
||||||
cmd(53, pos, wh, color);
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
render.doc = "Draw shapes in screen space.";
|
|
||||||
render.circle.doc = "Draw a circle at pos, with a given radius and color.";
|
|
||||||
render.cross.doc = "Draw a cross centered at pos, with arm length size.";
|
|
||||||
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
|
|
||||||
render.poly.doc = "Draw a concave polygon from a set of points.";
|
|
||||||
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
|
|
||||||
render.box.doc = "Draw a box centered at pos, with width and height in the tuple wh.";
|
|
||||||
render.line.doc = "Draw a line from a set of points, and a given thickness.";
|
|
||||||
|
|
||||||
var Debug = {
|
var Debug = {
|
||||||
fn_break(fn, obj) {
|
fn_break(fn, obj) {
|
||||||
if (typeof fn !== 'function') return;
|
if (typeof fn !== 'function') return;
|
||||||
|
@ -339,7 +265,7 @@ Time.doc.time = "Seconds elapsed since the game started.";
|
||||||
Time.doc.pause = "Pause the game by setting the timescale to 0; remembers the current timescale on play.";
|
Time.doc.pause = "Pause the game by setting the timescale to 0; remembers the current timescale on play.";
|
||||||
Time.doc.play = "Resume the game after using Time.pause.";
|
Time.doc.play = "Resume the game after using Time.pause.";
|
||||||
|
|
||||||
Player.players[0].control(DebugControls);
|
player[0].control(DebugControls);
|
||||||
Register.gui.register(Debug.draw, Debug);
|
Register.gui.register(Debug.draw, Debug);
|
||||||
|
|
||||||
Debug.api = {};
|
Debug.api = {};
|
||||||
|
|
|
@ -32,6 +32,7 @@ function ediff(from,to)
|
||||||
if (typeof v === 'function') return;
|
if (typeof v === 'function') return;
|
||||||
if (typeof v === 'undefined') return;
|
if (typeof v === 'undefined') return;
|
||||||
|
|
||||||
|
|
||||||
if (Array.isArray(v)) {
|
if (Array.isArray(v)) {
|
||||||
if (!Array.isArray(to[key]) || v.length !== to[key].length) {
|
if (!Array.isArray(to[key]) || v.length !== to[key].length) {
|
||||||
var r = ediff(v,[]);
|
var r = ediff(v,[]);
|
||||||
|
@ -54,7 +55,8 @@ function ediff(from,to)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof v === 'number') {
|
if (typeof v === 'number') {
|
||||||
if (!to || v !== to[key])
|
if (!isFinite(v)) v = null; // Squash infinity to null
|
||||||
|
if (v !== to[key])
|
||||||
ret[key] = v;
|
ret[key] = v;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +69,7 @@ function ediff(from,to)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ediff.doc = "Given a from and to object, returns an object that, if applied to from, will make it the same as to. Does not include deletion; it is only additive.";
|
ediff.doc = "Given a from and to object, returns an object that, if applied to from, will make it the same as to. Does not include deletion; it is only additive. If one element in an array is different, the entire array is copied. Squashes infinite numbers to null for use in JSON.";
|
||||||
|
|
||||||
function samediff(from, to)
|
function samediff(from, to)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,15 +2,12 @@
|
||||||
Editor-only variables on objects
|
Editor-only variables on objects
|
||||||
selectable
|
selectable
|
||||||
*/
|
*/
|
||||||
//prototypes.generate_ur('.');
|
|
||||||
|
|
||||||
var editor = {
|
var editor = {
|
||||||
toString() { return "editor"; },
|
toString() { return "editor"; },
|
||||||
grid_size: 100,
|
grid_size: 100,
|
||||||
ruler_mark_px: 100,
|
ruler_mark_px: 100,
|
||||||
grid_color: Color.green.alpha(0.3),
|
grid_color: Color.green.alpha(0.3),
|
||||||
|
|
||||||
dbg_ur: "arena.level1",
|
|
||||||
machine: undefined,
|
machine: undefined,
|
||||||
device_test: undefined,
|
device_test: undefined,
|
||||||
selectlist: [],
|
selectlist: [],
|
||||||
|
@ -40,10 +37,10 @@ var editor = {
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
if (!obj._ed.selectable) return undefined;
|
if (!obj._ed.selectable) return undefined;
|
||||||
|
|
||||||
if (obj.level !== this.edit_level) {
|
if (obj.master !== this.edit_level) {
|
||||||
var testlevel = obj.level;
|
var testlevel = obj.master;
|
||||||
while (testlevel && testlevel.level !== Primum && testlevel.level !== this.edit_level && testlevel !== testlevel.level)
|
while (testlevel && testlevel.master !== world && testlevel.master !== this.edit_level && testlevel !== testlevel.master)
|
||||||
testlevel = testlevel.level;
|
testlevel = testlevel.master;
|
||||||
return testlevel;
|
return testlevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +54,7 @@ var editor = {
|
||||||
curpanel: undefined,
|
curpanel: undefined,
|
||||||
|
|
||||||
check_level_nested() {
|
check_level_nested() {
|
||||||
if (this.edit_level.level) {
|
if (this.edit_level.master) {
|
||||||
this.openpanel(gen_notify("Can't close a nested level. Save up to the root before continuing."));
|
this.openpanel(gen_notify("Can't close a nested level. Save up to the root before continuing."));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -186,45 +183,45 @@ var editor = {
|
||||||
|
|
||||||
start_play_ed() {
|
start_play_ed() {
|
||||||
this.stash = this.desktop.instance_obj();
|
this.stash = this.desktop.instance_obj();
|
||||||
Primum.clear();
|
world.clear();
|
||||||
load("config.js");
|
global.mixin("config.js");
|
||||||
Game.play();
|
Game.play();
|
||||||
Player.players[0].uncontrol(this);
|
player[0].uncontrol(this);
|
||||||
Player.players[0].control(limited_editor);
|
player[0].control(limited_editor);
|
||||||
editor.cbs.forEach(cb => cb());
|
editor.cbs.forEach(cb => cb());
|
||||||
editor.cbs = [];
|
editor.cbs = [];
|
||||||
load("predbg.js");
|
global.mixin("predbg.js");
|
||||||
console.warn(`starting game with ${this.dbg_ur}`);
|
console.warn(`starting game with ${this.dbg_ur}`);
|
||||||
editor.dbg_play = Primum.spawn(this.dbg_ur);
|
editor.dbg_play = world.spawn(this.dbg_ur);
|
||||||
editor.dbg_play.pos = [0,0];
|
editor.dbg_play.pos = [0,0];
|
||||||
load("debug.js");
|
global.mixin("debug.js");
|
||||||
},
|
},
|
||||||
|
|
||||||
start_play() {
|
start_play() {
|
||||||
Primum.clear();
|
world.clear();
|
||||||
load("config.js");
|
global.mixin("config.js");
|
||||||
Game.play();
|
Game.play();
|
||||||
Player.players[0].uncontrol(this);
|
player[0].uncontrol(this);
|
||||||
Player.players[0].control(limited_editor);
|
player[0].control(limited_editor);
|
||||||
editor.cbs.forEach(cb=>cb());
|
editor.cbs.forEach(cb=>cb());
|
||||||
editor.cbs = [];
|
editor.cbs = [];
|
||||||
load("game.js");
|
global.mixin("game.js");
|
||||||
},
|
},
|
||||||
|
|
||||||
cbs: [],
|
cbs: [],
|
||||||
|
|
||||||
enter_editor() {
|
enter_editor() {
|
||||||
Game.pause();
|
Game.pause();
|
||||||
Player.players[0].control(this);
|
player[0].control(this);
|
||||||
Player.players[0].uncontrol(limited_editor);
|
player[0].uncontrol(limited_editor);
|
||||||
|
|
||||||
editor.cbs.push(Register.gui.register(editor.gui.bind(editor)));
|
editor.cbs.push(Register.gui.register(editor.gui.bind(editor)));
|
||||||
editor.cbs.push(Register.draw.register(editor.draw.bind(editor)));
|
editor.cbs.push(Register.draw.register(editor.draw.bind(editor)));
|
||||||
editor.cbs.push(Register.debug.register(editor.ed_debug.bind(editor)));
|
editor.cbs.push(Register.debug.register(editor.ed_debug.bind(editor)));
|
||||||
editor.cbs.push(Register.update.register(GUI.controls.update, GUI.controls));
|
editor.cbs.push(Register.update.register(GUI.controls.update, GUI.controls));
|
||||||
|
|
||||||
this.desktop = Primum.spawn(ur.arena);
|
this.desktop = world.spawn();
|
||||||
Primum.rename_obj(this.desktop.toString(), "desktop");
|
world.rename_obj(this.desktop.toString(), "desktop");
|
||||||
this.edit_level = this.desktop;
|
this.edit_level = this.desktop;
|
||||||
editor.edit_level._ed.selectable = false;
|
editor.edit_level._ed.selectable = false;
|
||||||
if (this.stash) {
|
if (this.stash) {
|
||||||
|
@ -232,7 +229,7 @@ var editor = {
|
||||||
Object.dainty_assign(this.desktop, this.stash);
|
Object.dainty_assign(this.desktop, this.stash);
|
||||||
}
|
}
|
||||||
this.selectlist = [];
|
this.selectlist = [];
|
||||||
editor.camera = Primum.spawn(ur.camera2d);
|
editor.camera = world.spawn("scripts/camera2d.jso");
|
||||||
editor.camera._ed.selectable = false;
|
editor.camera._ed.selectable = false;
|
||||||
Game.view_camera(editor.camera);
|
Game.view_camera(editor.camera);
|
||||||
},
|
},
|
||||||
|
@ -244,11 +241,11 @@ var editor = {
|
||||||
openpanel(panel) {
|
openpanel(panel) {
|
||||||
if (this.curpanel) {
|
if (this.curpanel) {
|
||||||
this.curpanel.close();
|
this.curpanel.close();
|
||||||
Player.players[0].uncontrol(this.curpanel);
|
player[0].uncontrol(this.curpanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.curpanel = panel;
|
this.curpanel = panel;
|
||||||
Player.players[0].control(this.curpanel);
|
player[0].control(this.curpanel);
|
||||||
this.curpanel.open();
|
this.curpanel.open();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -271,6 +268,7 @@ var editor = {
|
||||||
},
|
},
|
||||||
|
|
||||||
snapshot() {
|
snapshot() {
|
||||||
|
return; // TODO: Implement
|
||||||
var dif = this.edit_level.json_obj();
|
var dif = this.edit_level.json_obj();
|
||||||
if (!dif) return;
|
if (!dif) return;
|
||||||
|
|
||||||
|
@ -338,10 +336,6 @@ var editor = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
load_desktop(d) {
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
draw_objects_names(obj,root,depth){
|
draw_objects_names(obj,root,depth){
|
||||||
if (!obj) return;
|
if (!obj) return;
|
||||||
if (!obj.objects) return;
|
if (!obj.objects) return;
|
||||||
|
@ -358,13 +352,13 @@ var editor = {
|
||||||
get sel_comp() { return this._sel_comp; },
|
get sel_comp() { return this._sel_comp; },
|
||||||
set sel_comp(x) {
|
set sel_comp(x) {
|
||||||
if (this._sel_comp)
|
if (this._sel_comp)
|
||||||
Player.players[0].uncontrol(this._sel_comp);
|
player[0].uncontrol(this._sel_comp);
|
||||||
|
|
||||||
this._sel_comp = x;
|
this._sel_comp = x;
|
||||||
|
|
||||||
if (this._sel_comp) {
|
if (this._sel_comp) {
|
||||||
console.info("sel comp is now " + this._sel_comp);
|
console.info("sel comp is now " + this._sel_comp);
|
||||||
Player.players[0].control(this._sel_comp);
|
player[0].control(this._sel_comp);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -413,9 +407,9 @@ var editor = {
|
||||||
var clvl = thiso;
|
var clvl = thiso;
|
||||||
|
|
||||||
var lvlchain = [];
|
var lvlchain = [];
|
||||||
while (clvl !== Primum) {
|
while (clvl !== world) {
|
||||||
lvlchain.push(clvl);
|
lvlchain.push(clvl);
|
||||||
clvl = clvl.level;
|
clvl = clvl.master;
|
||||||
}
|
}
|
||||||
lvlchain.push(clvl);
|
lvlchain.push(clvl);
|
||||||
|
|
||||||
|
@ -450,11 +444,7 @@ var editor = {
|
||||||
GUI.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
|
GUI.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
|
||||||
|
|
||||||
this.selectlist.forEach(function(x) {
|
this.selectlist.forEach(function(x) {
|
||||||
var sname = x.__proto__.toString();
|
GUI.text(x.urstr(), x.screenpos().add([0, 32]), 1, Color.editor.ur);
|
||||||
x.check_dirty();
|
|
||||||
if (x._ed.dirty) sname += "*";
|
|
||||||
|
|
||||||
GUI.text(sname, x.screenpos().add([0, 32]), 1, Color.editor.ur);
|
|
||||||
GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white);
|
GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white);
|
||||||
render.cross(x.screenpos(), 10, Color.blue);
|
render.cross(x.screenpos(), 10, Color.blue);
|
||||||
});
|
});
|
||||||
|
@ -532,9 +522,7 @@ var editor = {
|
||||||
lvl_history: [],
|
lvl_history: [],
|
||||||
|
|
||||||
load(file) {
|
load(file) {
|
||||||
var ur = prototypes.get_ur(file);
|
var obj = editor.edit_level.spawn(Object.access(ur, file));
|
||||||
if (!ur) return;
|
|
||||||
var obj = editor.edit_level.spawn(ur);
|
|
||||||
obj.set_worldpos(Mouse.worldpos);
|
obj.set_worldpos(Mouse.worldpos);
|
||||||
this.selectlist = [obj];
|
this.selectlist = [obj];
|
||||||
},
|
},
|
||||||
|
@ -550,10 +538,11 @@ var editor = {
|
||||||
/* Checking to save an entity as a subtype. */
|
/* Checking to save an entity as a subtype. */
|
||||||
/* sub is the name of the (sub)type; obj is the object to save it as */
|
/* sub is the name of the (sub)type; obj is the object to save it as */
|
||||||
saveas_check(sub, obj) {
|
saveas_check(sub, obj) {
|
||||||
|
return;
|
||||||
if (!sub) return;
|
if (!sub) return;
|
||||||
obj ??= editor.selectlist[0];
|
obj ??= editor.selectlist[0];
|
||||||
|
|
||||||
var curur = prototypes.get_ur(sub);
|
// var curur = prototypes.get_ur(sub);
|
||||||
|
|
||||||
if (curur) {
|
if (curur) {
|
||||||
notifypanel.action = editor.saveas;
|
notifypanel.action = editor.saveas;
|
||||||
|
@ -570,7 +559,7 @@ var editor = {
|
||||||
editor.selectlist = [nobj];
|
editor.selectlist = [nobj];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editor.edit_level = editor.edit_level.level;
|
editor.edit_level = editor.edit_level.master;
|
||||||
}
|
}
|
||||||
|
|
||||||
var t = obj.transform();
|
var t = obj.transform();
|
||||||
|
@ -633,7 +622,7 @@ editor.inputs.n = function() {
|
||||||
var o = editor.try_select();
|
var o = editor.try_select();
|
||||||
if (!o) return;
|
if (!o) return;
|
||||||
if (o === editor.selectlist[0]) return;
|
if (o === editor.selectlist[0]) return;
|
||||||
if (o.level !== editor.selectlist[0].level) return;
|
if (o.master !== editor.selectlist[0].master) return;
|
||||||
|
|
||||||
var tpos = editor.selectlist[0].pos;
|
var tpos = editor.selectlist[0].pos;
|
||||||
tpos.x *= -1;
|
tpos.x *= -1;
|
||||||
|
@ -646,7 +635,7 @@ editor.inputs['M-n'] = function()
|
||||||
var o = editor.try_select();
|
var o = editor.try_select();
|
||||||
if (!o) return;
|
if (!o) return;
|
||||||
if (o === editor.selectlist[0]) return;
|
if (o === editor.selectlist[0]) return;
|
||||||
if (o.level !== editor.selectlist[0].level) return;
|
if (o.master !== editor.selectlist[0].master) return;
|
||||||
|
|
||||||
var tpos = editor.selectlist[0].pos;
|
var tpos = editor.selectlist[0].pos;
|
||||||
tpos.y *= -1;
|
tpos.y *= -1;
|
||||||
|
@ -662,7 +651,7 @@ editor.inputs['h'] = function() {
|
||||||
};
|
};
|
||||||
editor.inputs['h'].doc = "Toggle object hidden.";
|
editor.inputs['h'].doc = "Toggle object hidden.";
|
||||||
|
|
||||||
editor.inputs['C-h'] = function() { Primum.objects.forEach(function(x) { x.visible = true; }); };
|
editor.inputs['C-h'] = function() { world.objects.forEach(function(x) { x.visible = true; }); };
|
||||||
editor.inputs['C-h'].doc = "Unhide all objects.";
|
editor.inputs['C-h'].doc = "Unhide all objects.";
|
||||||
|
|
||||||
editor.inputs['C-e'] = function() { editor.openpanel(assetexplorer); };
|
editor.inputs['C-e'] = function() { editor.openpanel(assetexplorer); };
|
||||||
|
@ -733,9 +722,9 @@ editor.inputs['C-f'] = function() {
|
||||||
editor.inputs['C-f'].doc = "Tunnel into the selected level object to edit it.";
|
editor.inputs['C-f'].doc = "Tunnel into the selected level object to edit it.";
|
||||||
|
|
||||||
editor.inputs['C-F'] = function() {
|
editor.inputs['C-F'] = function() {
|
||||||
if (editor.edit_level.level === Primum) return;
|
if (editor.edit_level.master === world) return;
|
||||||
|
|
||||||
editor.edit_level = editor.edit_level.level;
|
editor.edit_level = editor.edit_level.master;
|
||||||
editor.unselect();
|
editor.unselect();
|
||||||
editor.reset_undos();
|
editor.reset_undos();
|
||||||
};
|
};
|
||||||
|
@ -807,7 +796,7 @@ editor.inputs['C-space'] = function() {
|
||||||
editor.inputs['C-space'].doc = "Search to execute a specific command.";
|
editor.inputs['C-space'].doc = "Search to execute a specific command.";
|
||||||
|
|
||||||
editor.inputs['M-m'] = function() {
|
editor.inputs['M-m'] = function() {
|
||||||
// Player.players[0].control(rebinder);
|
// player[0].control(rebinder);
|
||||||
};
|
};
|
||||||
editor.inputs['M-m'].doc = "Rebind a shortcut. Usage: M-m SHORTCUT TARGET";
|
editor.inputs['M-m'].doc = "Rebind a shortcut. Usage: M-m SHORTCUT TARGET";
|
||||||
|
|
||||||
|
@ -838,7 +827,8 @@ editor.inputs['C-s'] = function() {
|
||||||
var savejs = saveobj.json_obj();
|
var savejs = saveobj.json_obj();
|
||||||
Object.merge(saveobj.__proto__, savejs);
|
Object.merge(saveobj.__proto__, savejs);
|
||||||
if (savejs.objects) saveobj.__proto__.objects = savejs.objects;
|
if (savejs.objects) saveobj.__proto__.objects = savejs.objects;
|
||||||
var path = prototypes.ur_stem(saveobj.ur.toString()) + ".json";
|
// var path = prototypes.ur_stem(saveobj.ur.toString()) + ".json";
|
||||||
|
path = "CHANGETHIS";
|
||||||
|
|
||||||
io.slurpwrite(path, JSON.stringify(saveobj.__proto__,null,1));
|
io.slurpwrite(path, JSON.stringify(saveobj.__proto__,null,1));
|
||||||
console.warn(`Wrote to file ${path}`);
|
console.warn(`Wrote to file ${path}`);
|
||||||
|
@ -1053,7 +1043,7 @@ editor.inputs['C-M-lm'] = function()
|
||||||
{
|
{
|
||||||
var go = physics.pos_query(Mouse.worldpos);
|
var go = physics.pos_query(Mouse.worldpos);
|
||||||
if (!go) return;
|
if (!go) return;
|
||||||
editor.edit_level = go.level;
|
editor.edit_level = go.master;
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.inputs['C-M-mm'] = function() {
|
editor.inputs['C-M-mm'] = function() {
|
||||||
|
@ -1380,7 +1370,7 @@ var inputpanel = {
|
||||||
start() {},
|
start() {},
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
Player.players[0].uncontrol(this);
|
player[0].uncontrol(this);
|
||||||
this.on = false;
|
this.on = false;
|
||||||
if ('on_close' in this)
|
if ('on_close' in this)
|
||||||
this.on_close();
|
this.on_close();
|
||||||
|
@ -1505,6 +1495,7 @@ var replpanel = Object.copy(inputpanel, {
|
||||||
this.value = "";
|
this.value = "";
|
||||||
this.caret = 0;
|
this.caret = 0;
|
||||||
var ret = function() {return eval(ecode);}.call(repl_obj);
|
var ret = function() {return eval(ecode);}.call(repl_obj);
|
||||||
|
if (typeof ret === 'object') ret = json.encode(ret,null,1);
|
||||||
console.say(ret);
|
console.say(ret);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1797,7 +1788,7 @@ var openlevelpanel = Object.copy(inputpanel, {
|
||||||
},
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.allassets = prototypes.list.sort();
|
this.allassets = ur._list.sort();
|
||||||
this.assets = this.allassets.slice();
|
this.assets = this.allassets.slice();
|
||||||
this.caret = 0;
|
this.caret = 0;
|
||||||
var click_ur = function(btn) {
|
var click_ur = function(btn) {
|
||||||
|
@ -1954,7 +1945,7 @@ var entitylistpanel = Object.copy(inputpanel, {
|
||||||
title: "Level object list",
|
title: "Level object list",
|
||||||
level: {},
|
level: {},
|
||||||
start() {
|
start() {
|
||||||
this.level = editor.edit_level;
|
this.master = editor.edit_level;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1978,9 +1969,9 @@ limited_editor.inputs['M-p'] = function()
|
||||||
|
|
||||||
limited_editor.inputs['C-q'] = function()
|
limited_editor.inputs['C-q'] = function()
|
||||||
{
|
{
|
||||||
Primum.clear();
|
world.clear();
|
||||||
load("editorconfig.js");
|
global.mixin("editorconfig.js");
|
||||||
load("dbgret.js");
|
global.mixin("dbgret.js");
|
||||||
|
|
||||||
editor.enter_editor();
|
editor.enter_editor();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,19 +14,28 @@ function use(file)
|
||||||
}
|
}
|
||||||
use.files = {};
|
use.files = {};
|
||||||
|
|
||||||
|
function include(file,that)
|
||||||
|
{
|
||||||
|
if (!that) return;
|
||||||
|
var c = io.slurp(file);
|
||||||
|
eval_env(c, that, file);
|
||||||
|
}
|
||||||
|
|
||||||
function eval_env(script, env, file)
|
function eval_env(script, env, file)
|
||||||
{
|
{
|
||||||
env ??= {};
|
env ??= {};
|
||||||
file ??= "SCRIPT";
|
file ??= "SCRIPT";
|
||||||
// script = `(function() { ${script} })();`;
|
script = `(function() { ${script} }).call(this);`;
|
||||||
eval(`(function() { ${script} }).call(env);`);
|
return cmd(123,script,env,file);
|
||||||
// cmd(123,script,global,file);
|
|
||||||
// return eval(script);
|
|
||||||
|
|
||||||
// return function(str) { return eval(str); }.call(env, script);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_env.dov = `Counterpart to /load_env/, but with a string.`;
|
eval_env.dov = `Counterpart to /load_env/, but with a string.`;
|
||||||
|
|
||||||
|
function feval_env(file, env)
|
||||||
|
{
|
||||||
|
eval_env(io.slurp(file), env, file);
|
||||||
|
}
|
||||||
|
|
||||||
function load_env(file,env)
|
function load_env(file,env)
|
||||||
{
|
{
|
||||||
env ??= global;
|
env ??= global;
|
||||||
|
@ -43,12 +52,12 @@ var load = use;
|
||||||
|
|
||||||
Object.assign(global, use("scripts/base.js"));
|
Object.assign(global, use("scripts/base.js"));
|
||||||
global.obscure('global');
|
global.obscure('global');
|
||||||
global.mixin(use("scripts/std.js"));
|
global.mixin("scripts/std.js");
|
||||||
global.mixin(use("scripts/diff.js"));
|
global.mixin("scripts/diff.js");
|
||||||
|
|
||||||
console.level = 1;
|
console.level = 1;
|
||||||
|
|
||||||
global.mixin(use("scripts/color.js"));
|
global.mixin("scripts/color.js");
|
||||||
|
|
||||||
var prosperon = {};
|
var prosperon = {};
|
||||||
prosperon.version = cmd(255);
|
prosperon.version = cmd(255);
|
||||||
|
@ -104,7 +113,7 @@ Range is given by a semantic versioning number, prefixed with nothing, a ~, or a
|
||||||
^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`;
|
^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`;
|
||||||
|
|
||||||
|
|
||||||
global.mixin(use("scripts/gui.js"));
|
global.mixin("scripts/gui.js");
|
||||||
|
|
||||||
var timer = {
|
var timer = {
|
||||||
update(dt) {
|
update(dt) {
|
||||||
|
@ -130,61 +139,13 @@ var timer = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
load("scripts/tween.js");
|
global.mixin("scripts/tween.js");
|
||||||
|
global.mixin("scripts/render.js");
|
||||||
var render = {
|
global.mixin("scripts/physics.js");
|
||||||
normal() { cmd(67);},
|
global.mixin("scripts/input.js");
|
||||||
wireframe() { cmd(68); },
|
global.mixin("scripts/sound.js");
|
||||||
pass() { },
|
global.mixin("scripts/ai.js");
|
||||||
};
|
global.mixin("scripts/geometry.js");
|
||||||
|
|
||||||
render.doc = {
|
|
||||||
doc: "Functions for rendering modes.",
|
|
||||||
normal: "Final render with all lighting.",
|
|
||||||
wireframe: "Show only wireframes of models."
|
|
||||||
};
|
|
||||||
|
|
||||||
render.device = {
|
|
||||||
pc: [1920,1080],
|
|
||||||
macbook_m2: [2560,1664, 13.6],
|
|
||||||
ds_top: [400,240, 3.53],
|
|
||||||
ds_bottom: [320,240, 3.02],
|
|
||||||
playdate: [400,240,2.7],
|
|
||||||
switch: [1280,720, 6.2],
|
|
||||||
switch_lite: [1280,720,5.5],
|
|
||||||
switch_oled: [1280,720,7],
|
|
||||||
dsi: [256,192,3.268],
|
|
||||||
ds: [256,192, 3],
|
|
||||||
dsixl: [256,192,4.2],
|
|
||||||
ipad_air_m2: [2360,1640, 11.97],
|
|
||||||
iphone_se: [1334, 750, 4.7],
|
|
||||||
iphone_12_pro: [2532,1170,6.06],
|
|
||||||
iphone_15: [2556,1179,6.1],
|
|
||||||
gba: [240,160,2.9],
|
|
||||||
gameboy: [160,144,2.48],
|
|
||||||
gbc: [160,144,2.28],
|
|
||||||
steamdeck: [1280,800,7],
|
|
||||||
vita: [960,544,5],
|
|
||||||
psp: [480,272,4.3],
|
|
||||||
imac_m3: [4480,2520,23.5],
|
|
||||||
macbook_pro_m3: [3024,1964, 14.2],
|
|
||||||
ps1: [320,240,5],
|
|
||||||
ps2: [640,480],
|
|
||||||
snes: [256,224],
|
|
||||||
gamecube: [640,480],
|
|
||||||
n64: [320,240],
|
|
||||||
c64: [320,200],
|
|
||||||
macintosh: [512,342,9],
|
|
||||||
gamegear: [160,144,3.2],
|
|
||||||
};
|
|
||||||
|
|
||||||
render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
|
|
||||||
|
|
||||||
global.mixin(use("scripts/physics.js"));
|
|
||||||
global.mixin(use("scripts/input.js"));
|
|
||||||
global.mixin(use("scripts/sound.js"));
|
|
||||||
global.mixin(use("scripts/ai.js"));
|
|
||||||
global.mixin(use("scripts/geometry.js"));
|
|
||||||
|
|
||||||
var Register = {
|
var Register = {
|
||||||
kbm_input(mode, btn, state, ...args) {
|
kbm_input(mode, btn, state, ...args) {
|
||||||
|
@ -194,15 +155,15 @@ var Register = {
|
||||||
|
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case "emacs":
|
case "emacs":
|
||||||
Player.players[0].raw_input(btn, state, ...args);
|
player[0].raw_input(btn, state, ...args);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "mouse":
|
case "mouse":
|
||||||
Player.players[0].mouse_input(btn, state, ...args);
|
player[0].mouse_input(btn, state, ...args);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "char":
|
case "char":
|
||||||
Player.players[0].char_input(btn);
|
player[0].char_input(btn);
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -275,7 +236,7 @@ Register.add_cb(10, "draw");
|
||||||
|
|
||||||
register(9, console.stack, this);
|
register(9, console.stack, this);
|
||||||
|
|
||||||
Register.gamepad_playermap[0] = Player.players[0];
|
Register.gamepad_playermap[0] = player[0];
|
||||||
|
|
||||||
var Event = {
|
var Event = {
|
||||||
events: {},
|
events: {},
|
||||||
|
@ -320,21 +281,21 @@ var Window = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Window.screen2world = function(screenpos) {
|
Window.screen2world = function(screenpos) {
|
||||||
// if (Game.camera)
|
if (Game.camera)
|
||||||
// return Game.camera.view2world(screenpos);
|
return Game.camera.view2world(screenpos);
|
||||||
|
|
||||||
return screenpos;
|
return screenpos;
|
||||||
}
|
}
|
||||||
Window.world2screen = function(worldpos) {
|
Window.world2screen = function(worldpos) {
|
||||||
return worldpos;
|
|
||||||
return Game.camera.world2view(worldpos);
|
return Game.camera.world2view(worldpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window.icon = function(path) { cmd(90, path); };
|
Window.icon = function(path) { cmd(90, path); };
|
||||||
Window.icon.doc = "Set the icon of the window using the PNG image at path.";
|
Window.icon.doc = "Set the icon of the window using the PNG image at path.";
|
||||||
|
|
||||||
global.mixin(use("scripts/debug.js"));
|
global.mixin("scripts/debug.js");
|
||||||
global.mixin(use("scripts/spline.js"));
|
global.mixin("scripts/spline.js");
|
||||||
global.mixin(use("scripts/components.js"));
|
global.mixin("scripts/components.js");
|
||||||
|
|
||||||
var Game = {
|
var Game = {
|
||||||
engine_start(fn) {
|
engine_start(fn) {
|
||||||
|
@ -420,31 +381,33 @@ Window.doc.boundingbox = "Boundingbox of the window, with top and right being it
|
||||||
|
|
||||||
Register.update.register(Game.exec, Game);
|
Register.update.register(Game.exec, Game);
|
||||||
|
|
||||||
global.mixin(use("scripts/entity.js"));
|
global.mixin("scripts/actor.js");
|
||||||
|
global.mixin("scripts/entity.js");
|
||||||
|
|
||||||
function world_start() {
|
function world_start() {
|
||||||
globalThis.Primum = Object.create(gameobject);
|
globalThis.world = Object.create(gameobject);
|
||||||
Primum.objects = {};
|
world.objects = {};
|
||||||
Primum.check_dirty = function() {};
|
world.check_dirty = function() {};
|
||||||
Primum.namestr = function(){};
|
world.namestr = function(){};
|
||||||
Primum._ed = {
|
world._ed = {
|
||||||
selectable:false,
|
selectable:false,
|
||||||
dirty:false,
|
dirty:false,
|
||||||
};
|
};
|
||||||
Primum.toString = function() { return "Primum"; };
|
world.toString = function() { return "world"; };
|
||||||
Primum.ur = "Primum";
|
world.master = gameobject;
|
||||||
Primum.kill = function() { this.clear(); };
|
world.ur = "world";
|
||||||
Primum.phys = 2;
|
world.kill = function() { this.clear(); };
|
||||||
|
world.phys = 2;
|
||||||
|
|
||||||
gameobject.level = Primum;
|
gameobject.level = world;
|
||||||
gameobject.body = make_gameobject();
|
gameobject.body = make_gameobject();
|
||||||
cmd(113,gameobject.body, gameobject);
|
cmd(113,gameobject.body, gameobject);
|
||||||
Object.hide(gameobject, 'timescale');
|
Object.hide(gameobject, 'timescale');
|
||||||
|
var cam = world.spawn("scripts/camera2d.jso");
|
||||||
global.world = Primum;
|
Game.view_camera(cam);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.mixin(use("scripts/physics.js"));
|
global.mixin("scripts/physics.js");
|
||||||
|
|
||||||
Game.view_camera = function(cam)
|
Game.view_camera = function(cam)
|
||||||
{
|
{
|
||||||
|
@ -452,8 +415,6 @@ Game.view_camera = function(cam)
|
||||||
cmd(61, Game.camera.body);
|
cmd(61, Game.camera.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
prototypes.generate_ur('scripts/camera.jso');
|
|
||||||
|
|
||||||
Window.title(`Prosperon v${prosperon.version}`);
|
Window.title(`Prosperon v${prosperon.version}`);
|
||||||
Window.width = 1280;
|
Window.width = 1280;
|
||||||
Window.height = 720;
|
Window.height = 720;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
prosperon.obj_unique_name = function(name, obj)
|
function obj_unique_name(name, obj)
|
||||||
{
|
{
|
||||||
name = name.replaceAll('.', '_');
|
name = name.replaceAll('.', '_');
|
||||||
if (!(name in obj)) return name;
|
if (!(name in obj)) return name;
|
||||||
|
@ -11,92 +11,55 @@ prosperon.obj_unique_name = function(name, obj)
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
var actor = {};
|
function check_registers(obj)
|
||||||
var a_db = {};
|
|
||||||
|
|
||||||
actor.spawn = function(script, config){
|
|
||||||
if (typeof script !== 'string') return undefined;
|
|
||||||
if (!a_db[script]) a_db[script] = io.slurp(script);
|
|
||||||
var padawan = Object.create(actor);
|
|
||||||
eval_env(a_db[script], padawan);
|
|
||||||
|
|
||||||
if (typeof config === 'object')
|
|
||||||
Object.merge(padawan,config);
|
|
||||||
|
|
||||||
padawan.padawans = [];
|
|
||||||
padawan.timers = [];
|
|
||||||
padawan.master = this;
|
|
||||||
Object.hide(padawan, "master","timers", "padawans");
|
|
||||||
this.padawans.push(padawan);
|
|
||||||
return padawan;
|
|
||||||
};
|
|
||||||
|
|
||||||
actor.spawn.doc = `Create a new actor, using this actor as the master, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`;
|
|
||||||
|
|
||||||
actor.timers = [];
|
|
||||||
actor.kill = function(){
|
|
||||||
if (this.__dead__) return;
|
|
||||||
this.timers.forEach(t => t.kill());
|
|
||||||
if (this.master)
|
|
||||||
delete this.master[this.toString()];
|
|
||||||
this.padawans.forEach(p => p.kill());
|
|
||||||
this.padawans = [];
|
|
||||||
this.__dead__ = true;
|
|
||||||
if (typeof this.die === 'function') this.die();
|
|
||||||
};
|
|
||||||
|
|
||||||
actor.kill.doc = `Remove this actor and all its padawans from existence.`;
|
|
||||||
|
|
||||||
actor.delay = function(fn, seconds) {
|
|
||||||
var t = Object.create(timer);
|
|
||||||
t.remain = seconds;
|
|
||||||
t.kill = () => {
|
|
||||||
timer.kill.call(t);
|
|
||||||
delete this.timers[t.toString()];
|
|
||||||
}
|
|
||||||
t.fire = () => {
|
|
||||||
if (this.__dead__) return;
|
|
||||||
fn();
|
|
||||||
t.kill();
|
|
||||||
};
|
|
||||||
Register.appupdate.register(t.update, t);
|
|
||||||
this.timers.push(t);
|
|
||||||
return function() { t.kill(); };
|
|
||||||
};
|
|
||||||
|
|
||||||
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
|
|
||||||
|
|
||||||
actor.master = undefined;
|
|
||||||
|
|
||||||
actor.padawans = [];
|
|
||||||
|
|
||||||
actor.remaster = function(to){
|
|
||||||
delete this.master.padawans[this.toString()];
|
|
||||||
this.master = to;
|
|
||||||
to.padawans.push(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
global.app = Object.create(actor);
|
|
||||||
|
|
||||||
app.die = function()
|
|
||||||
{
|
{
|
||||||
Game.quit();
|
if (typeof obj.update === 'function')
|
||||||
|
obj.timers.push(Register.update.register(obj.update.bind(obj)));
|
||||||
|
|
||||||
|
if (typeof obj.physupdate === 'function')
|
||||||
|
obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj)));
|
||||||
|
|
||||||
|
if (typeof obj.collide === 'function')
|
||||||
|
register_collide(0, obj.collide.bind(obj), obj.body);
|
||||||
|
|
||||||
|
if (typeof obj.separate === 'function')
|
||||||
|
register_collide(3,obj.separate.bind(obj), obj.body);
|
||||||
|
|
||||||
|
if (typeof obj.draw === 'function')
|
||||||
|
obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj));
|
||||||
|
|
||||||
|
if (typeof obj.debug === 'function')
|
||||||
|
obj.timers.push(Register.debug.register(obj.debug.bind(obj)));
|
||||||
|
|
||||||
|
if (typeof obj.gui === 'function')
|
||||||
|
obj.timers.push(Register.gui.register(obj.gui.bind(obj)));
|
||||||
|
|
||||||
|
for (var k in obj) {
|
||||||
|
if (!k.startswith("on_")) continue;
|
||||||
|
var signal = k.fromfirst("on_");
|
||||||
|
Event.observe(signal, obj, obj[k]);
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.components.forEach(function(x) {
|
||||||
|
if (typeof x.collide === 'function')
|
||||||
|
register_collide(1, x.collide.bind(x), obj.body, x.shape);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var gameobject_impl = {
|
var gameobject_impl = {
|
||||||
get pos() {
|
get pos() {
|
||||||
Debug.assert(this.level, `Entity ${this.toString()} has no level.`);
|
Debug.assert(this.master, `Entity ${this.toString()} has no master.`);
|
||||||
return this.level.world2this(this.worldpos());
|
return this.master.world2this(this.worldpos());
|
||||||
},
|
},
|
||||||
|
|
||||||
set pos(x) {
|
set pos(x) {
|
||||||
Debug.assert(this.level, `Entity ${this.toString()} has no level.`);
|
Debug.assert(this.master, `Entity ${this.toString()} has no master.`);
|
||||||
this.set_worldpos(this.level.this2world(x));
|
this.set_worldpos(this.master.this2world(x));
|
||||||
},
|
},
|
||||||
|
|
||||||
get angle() {
|
get angle() {
|
||||||
Debug.assert(this.level, `No level set on ${this.toString()}`);
|
Debug.assert(this.master, `No master set on ${this.toString()}`);
|
||||||
return this.worldangle() - this.level.worldangle();
|
return this.worldangle() - this.master.worldangle();
|
||||||
},
|
},
|
||||||
|
|
||||||
set angle(x) {
|
set angle(x) {
|
||||||
|
@ -107,18 +70,18 @@ var gameobject_impl = {
|
||||||
x.pos = Vector.rotate(x.pos, diff);
|
x.pos = Vector.rotate(x.pos, diff);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sworldangle(x-this.level.worldangle());
|
this.sworldangle(x-this.master.worldangle());
|
||||||
},
|
},
|
||||||
|
|
||||||
get scale() {
|
get scale() {
|
||||||
Debug.assert(this.level, `No level set on ${this.toString()}`);
|
Debug.assert(this.master, `No master set on ${this.toString()}`);
|
||||||
var pscale;
|
var pscale = [1,1,1];
|
||||||
if (typeof this.__proto__.scale === 'object')
|
/* if (typeof this.master.scale === 'object')
|
||||||
pscale = this.__proto__.scale;
|
pscale = this.master.scale;
|
||||||
else
|
else
|
||||||
pscale = [1,1,1];
|
pscale = [1,1,1];
|
||||||
|
*/
|
||||||
return this.gscale().map((x,i) => x/(this.level.gscale()[i]*pscale[i]));
|
return this.gscale().map((x,i) => x/(this.master.gscale()[i]*pscale[i]));
|
||||||
},
|
},
|
||||||
|
|
||||||
set scale(x) {
|
set scale(x) {
|
||||||
|
@ -187,9 +150,11 @@ var gameobject = {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
check_dirty() {
|
check_dirty() {
|
||||||
|
// TODO: IMPLEMENT
|
||||||
|
return;
|
||||||
this._ed.urdiff = this.json_obj();
|
this._ed.urdiff = this.json_obj();
|
||||||
this._ed.dirty = !Object.empty(this._ed.urdiff);
|
this._ed.dirty = !Object.empty(this._ed.urdiff);
|
||||||
var lur = ur[this.level.ur];
|
var lur = ur[this.master.ur];
|
||||||
if (!lur) return;
|
if (!lur) return;
|
||||||
var lur = lur.objects[this.toString()];
|
var lur = lur.objects[this.toString()];
|
||||||
var d = ediff(this._ed.urdiff,lur);
|
var d = ediff(this._ed.urdiff,lur);
|
||||||
|
@ -202,6 +167,7 @@ var gameobject = {
|
||||||
selectable: false,
|
selectable: false,
|
||||||
dirty: false
|
dirty: false
|
||||||
},
|
},
|
||||||
|
|
||||||
namestr() {
|
namestr() {
|
||||||
var s = this.toString();
|
var s = this.toString();
|
||||||
if (this._ed.dirty)
|
if (this._ed.dirty)
|
||||||
|
@ -209,8 +175,14 @@ var gameobject = {
|
||||||
else s += "*";
|
else s += "*";
|
||||||
return s;
|
return s;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
urstr() {
|
||||||
|
if (this._ed.dirty) return "*"+this.ur;
|
||||||
|
return this.ur;
|
||||||
|
},
|
||||||
|
|
||||||
full_path() {
|
full_path() {
|
||||||
return this.path_from(Primum);
|
return this.path_from(world);
|
||||||
},
|
},
|
||||||
/* pin this object to the to object */
|
/* pin this object to the to object */
|
||||||
pin(to) {
|
pin(to) {
|
||||||
|
@ -270,12 +242,12 @@ var gameobject = {
|
||||||
|
|
||||||
path_from(o) {
|
path_from(o) {
|
||||||
var p = this.toString();
|
var p = this.toString();
|
||||||
var c = this.level;
|
var c = this.master;
|
||||||
while (c && c !== o && c !== Primum) {
|
while (c && c !== o && c !== world) {
|
||||||
p = c.toString() + "." + p;
|
p = c.toString() + "." + p;
|
||||||
c = c.level;
|
c = c.master;
|
||||||
}
|
}
|
||||||
if (c === Primum) p = "Primum." + p;
|
if (c === world) p = "world." + p;
|
||||||
return p;
|
return p;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -334,37 +306,110 @@ var gameobject = {
|
||||||
worldangle() { return Math.rad2turn(q_body(2,this.body)); },
|
worldangle() { return Math.rad2turn(q_body(2,this.body)); },
|
||||||
sworldangle(x) { set_body(0,this.body,Math.turn2rad(x)); },
|
sworldangle(x) { set_body(0,this.body,Math.turn2rad(x)); },
|
||||||
|
|
||||||
spawn_from_instance(inst) {
|
/* spawn an entity
|
||||||
return this.spawn(inst.ur, inst);
|
text can be:
|
||||||
},
|
the file path of a script
|
||||||
|
an ur object
|
||||||
|
nothing
|
||||||
|
*/
|
||||||
|
spawn(text) {
|
||||||
|
var ent = Object.create(gameobject);
|
||||||
|
|
||||||
spawn(ur, data) {
|
if (typeof text === 'object')
|
||||||
ur ??= gameobject;
|
text = text.name;
|
||||||
if (typeof ur === 'string') {
|
|
||||||
//ur = prototypes.get_ur(ur);
|
|
||||||
|
|
||||||
|
if (typeof text === 'undefined')
|
||||||
|
ent.ur = "new";
|
||||||
|
else if (typeof text !== 'string') {
|
||||||
|
console.error(`Must pass in an ur type or a string to make an entity.`);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (Object.access(ur,text))
|
||||||
|
ent.ur = text;
|
||||||
|
else if (io.exists(text))
|
||||||
|
ent.ur = "script";
|
||||||
|
else {
|
||||||
|
console.warn(`Cannot make an entity from '${text}'. Not a valid ur.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var go = ur.make(this, data);
|
Object.mixin(ent,gameobject_impl);
|
||||||
Object.hide(this, go.toString());
|
ent.body = make_gameobject();
|
||||||
return go;
|
|
||||||
|
ent.components = {};
|
||||||
|
ent.objects = {};
|
||||||
|
ent.timers = [];
|
||||||
|
|
||||||
|
ent.reparent(this);
|
||||||
|
|
||||||
|
ent._ed = {
|
||||||
|
selectable: true,
|
||||||
|
dirty: false,
|
||||||
|
inst: false,
|
||||||
|
urdiff: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
cmd(113, ent.body, ent); // set the internal obj reference to this obj
|
||||||
|
|
||||||
|
Object.hide(ent, 'ur','body', 'components', 'objects', '_ed', 'timers', 'master');
|
||||||
|
|
||||||
|
if (ent.ur === 'script')
|
||||||
|
eval_env(io.slurp(text), ent, ent.ur);
|
||||||
|
else if (ent.ur !== 'new')
|
||||||
|
apply_ur(ent.ur, ent);
|
||||||
|
|
||||||
|
for (var [prop,p] of Object.entries(ent)) {
|
||||||
|
if (!p) continue;
|
||||||
|
if (typeof p !== 'object') continue;
|
||||||
|
if (component.isComponent(p)) continue;
|
||||||
|
if (!p.comp) continue;
|
||||||
|
ent[prop] = component[p.comp].make(ent);
|
||||||
|
Object.merge(ent[prop], p);
|
||||||
|
ent.components[prop] = ent[prop];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
check_registers(ent);
|
||||||
|
|
||||||
|
if (typeof ent.load === 'function') ent.load();
|
||||||
|
if (typeof ent.start === 'function') ent.start();
|
||||||
|
|
||||||
|
var mur = Object.access(ur,ent.ur);
|
||||||
|
if (mur && !mur.proto)
|
||||||
|
mur.proto = json.decode(json.encode(ent));
|
||||||
|
|
||||||
|
if (!Object.empty(ent.objects)) {
|
||||||
|
var o = ent.objects;
|
||||||
|
delete ent.objects;
|
||||||
|
for (var i in o) {
|
||||||
|
say(`MAKING ${i}`);
|
||||||
|
var n = ent.spawn(ur[o[i].ur]);
|
||||||
|
ent.rename_obj(n.toString(), i);
|
||||||
|
delete o[i].ur;
|
||||||
|
Object.assign(n, o[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ent;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Reparent 'this' to be 'parent's child */
|
/* Reparent 'this' to be 'parent's child */
|
||||||
reparent(parent) {
|
reparent(parent) {
|
||||||
Debug.assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
|
Debug.assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
|
||||||
if (this.level === parent) {
|
if (this.master === parent) {
|
||||||
console.warn("not reparenting ...");
|
console.warn("not reparenting ...");
|
||||||
console.warn(`${this.level} is the same as ${parent}`);
|
console.warn(`${this.master} is the same as ${parent}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.level?.remove_obj(this);
|
this.master?.remove_obj(this);
|
||||||
|
|
||||||
this.level = parent;
|
this.master = parent;
|
||||||
|
|
||||||
function unique_name(list, obj) {
|
function unique_name(list, name) {
|
||||||
var str = obj.toString().replaceAll('.', '_');
|
name ??= "new_object";
|
||||||
|
var str = name.replaceAll('.', '_');
|
||||||
var n = 1;
|
var n = 1;
|
||||||
var t = str;
|
var t = str;
|
||||||
while (t in list) {
|
while (t in list) {
|
||||||
|
@ -374,7 +419,7 @@ var gameobject = {
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
|
||||||
var name = unique_name(parent, this.ur);
|
var name = unique_name(Object.keys(parent.objects), this.ur);
|
||||||
|
|
||||||
parent.objects[name] = this;
|
parent.objects[name] = this;
|
||||||
parent[name] = this;
|
parent[name] = this;
|
||||||
|
@ -391,7 +436,7 @@ var gameobject = {
|
||||||
|
|
||||||
components: {},
|
components: {},
|
||||||
objects: {},
|
objects: {},
|
||||||
level: undefined,
|
master: undefined,
|
||||||
|
|
||||||
pulse(vec) { set_body(4, this.body, vec);},
|
pulse(vec) { set_body(4, this.body, vec);},
|
||||||
shove(vec) { set_body(12,this.body,vec);},
|
shove(vec) { set_body(12,this.body,vec);},
|
||||||
|
@ -427,7 +472,7 @@ var gameobject = {
|
||||||
/* Make a unique object the same as its prototype */
|
/* Make a unique object the same as its prototype */
|
||||||
revert() {
|
revert() {
|
||||||
var jobj = this.json_obj();
|
var jobj = this.json_obj();
|
||||||
var lobj = this.level.__proto__.objects[this.toString()];
|
var lobj = this.master.__proto__.objects[this.toString()];
|
||||||
delete jobj.objects;
|
delete jobj.objects;
|
||||||
Object.keys(jobj).forEach(function(x) {
|
Object.keys(jobj).forEach(function(x) {
|
||||||
if (lobj && x in lobj)
|
if (lobj && x in lobj)
|
||||||
|
@ -438,53 +483,15 @@ var gameobject = {
|
||||||
this.sync();
|
this.sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
unregister() {
|
|
||||||
this.timers.forEach(t=>t());
|
|
||||||
this.timers = [];
|
|
||||||
},
|
|
||||||
|
|
||||||
check_registers(obj) {
|
|
||||||
obj.unregister();
|
|
||||||
|
|
||||||
if (typeof obj.update === 'function')
|
|
||||||
obj.timers.push(Register.update.register(obj.update.bind(obj)));
|
|
||||||
|
|
||||||
if (typeof obj.physupdate === 'function')
|
|
||||||
obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj)));
|
|
||||||
|
|
||||||
if (typeof obj.collide === 'function')
|
|
||||||
register_collide(0, obj.collide.bind(obj), obj.body);
|
|
||||||
|
|
||||||
if (typeof obj.separate === 'function')
|
|
||||||
register_collide(3,obj.separate.bind(obj), obj.body);
|
|
||||||
|
|
||||||
if (typeof obj.draw === 'function')
|
|
||||||
obj.timers.push(Register.draw.register(obj.draw.bind(obj), obj));
|
|
||||||
|
|
||||||
if (typeof obj.debug === 'function')
|
|
||||||
obj.timers.push(Register.debug.register(obj.debug.bind(obj)));
|
|
||||||
|
|
||||||
if (typeof obj.gui === 'function')
|
|
||||||
obj.timers.push(Register.gui.register(obj.gui.bind(obj)));
|
|
||||||
|
|
||||||
for (var k in obj) {
|
|
||||||
if (!k.startswith("on_")) continue;
|
|
||||||
var signal = k.fromfirst("on_");
|
|
||||||
Event.observe(signal, obj, obj[k]);
|
|
||||||
};
|
|
||||||
|
|
||||||
obj.components.forEach(function(x) {
|
|
||||||
if (typeof x.collide === 'function')
|
|
||||||
register_collide(1, x.collide.bind(x), obj.body, x.shape);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toString() { return "new_object"; },
|
toString() { return "new_object"; },
|
||||||
|
|
||||||
flipx() { return this.scale.x < 0; },
|
flipx() { return this.scale.x < 0; },
|
||||||
flipy() { return this.scale.y < 0; },
|
flipy() { return this.scale.y < 0; },
|
||||||
|
|
||||||
mirror(plane) {
|
mirror(plane) {
|
||||||
this.scale = Vector.reflect(this.scale, plane);
|
this.scale = Vector.reflect(this.scale, plane);
|
||||||
},
|
},
|
||||||
|
|
||||||
save:true,
|
save:true,
|
||||||
selectable:true,
|
selectable:true,
|
||||||
ed_locked:false,
|
ed_locked:false,
|
||||||
|
@ -524,17 +531,22 @@ var gameobject = {
|
||||||
|
|
||||||
/* The unique components of this object. Its diff. */
|
/* The unique components of this object. Its diff. */
|
||||||
json_obj() {
|
json_obj() {
|
||||||
var d = ediff(this,this.__proto__);
|
var u = Object.access(ur,this.ur);
|
||||||
|
if (!u) return {};
|
||||||
|
var proto = u.proto;
|
||||||
|
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
|
||||||
|
|
||||||
|
var d = ediff(thiso,proto);
|
||||||
|
|
||||||
d ??= {};
|
d ??= {};
|
||||||
|
|
||||||
var objects = {};
|
var objects = {};
|
||||||
this.__proto__.objects ??= {};
|
proto.objects ??= {};
|
||||||
var curobjs = {};
|
var curobjs = {};
|
||||||
for (var o in this.objects)
|
for (var o in this.objects)
|
||||||
curobjs[o] = this.objects[o].instance_obj();
|
curobjs[o] = this.objects[o].instance_obj();
|
||||||
|
|
||||||
var odiff = ediff(curobjs, this.__proto__.objects);
|
var odiff = ediff(curobjs, proto.objects);
|
||||||
if (odiff)
|
if (odiff)
|
||||||
d.objects = curobjs;
|
d.objects = curobjs;
|
||||||
|
|
||||||
|
@ -546,15 +558,19 @@ var gameobject = {
|
||||||
return d;
|
return d;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* The object needed to store an object as an instance of a level */
|
/* The object needed to store an object as an instance of a master */
|
||||||
instance_obj() {
|
instance_obj() {
|
||||||
var t = this.transform();
|
var t = this.transform();
|
||||||
// var j = this.json_obj();
|
|
||||||
// Object.assign(t,j);
|
|
||||||
t.ur = this.ur;
|
t.ur = this.ur;
|
||||||
return t;
|
return t;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
proto() {
|
||||||
|
var u = Object.access(ur,this.ur);
|
||||||
|
if (!u) return {};
|
||||||
|
return u.proto;
|
||||||
|
},
|
||||||
|
|
||||||
transform() {
|
transform() {
|
||||||
var t = {};
|
var t = {};
|
||||||
t.pos = this.pos;
|
t.pos = this.pos;
|
||||||
|
@ -562,7 +578,7 @@ var gameobject = {
|
||||||
t.angle = Math.places(this.angle,4);
|
t.angle = Math.places(this.angle,4);
|
||||||
if (t.angle === 0) delete t.angle;
|
if (t.angle === 0) delete t.angle;
|
||||||
t.scale = this.scale;
|
t.scale = this.scale;
|
||||||
t.scale = t.scale.map((x,i) => x/this.__proto__.scale[i]);
|
t.scale = t.scale.map((x,i) => x/this.proto().scale[i]);
|
||||||
t.scale = t.scale.map(x => Math.places(x,3));
|
t.scale = t.scale.map(x => Math.places(x,3));
|
||||||
if (t.scale.every(x=>x===1)) delete t.scale;
|
if (t.scale.every(x=>x===1)) delete t.scale;
|
||||||
return t;
|
return t;
|
||||||
|
@ -577,7 +593,7 @@ var gameobject = {
|
||||||
},
|
},
|
||||||
|
|
||||||
dup(diff) {
|
dup(diff) {
|
||||||
var n = this.level.spawn(this.__proto__);
|
var n = this.master.spawn(this.__proto__);
|
||||||
Object.totalmerge(n, this.instance_obj());
|
Object.totalmerge(n, this.instance_obj());
|
||||||
return n;
|
return n;
|
||||||
},
|
},
|
||||||
|
@ -592,9 +608,9 @@ var gameobject = {
|
||||||
Player.do_uncontrol(this);
|
Player.do_uncontrol(this);
|
||||||
register_collide(2, undefined, this.body);
|
register_collide(2, undefined, this.body);
|
||||||
|
|
||||||
if (this.level) {
|
if (this.master) {
|
||||||
this.level.remove_obj(this);
|
this.master.remove_obj(this);
|
||||||
this.level = undefined;
|
this.master = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.__proto__.instances)
|
if (this.__proto__.instances)
|
||||||
|
@ -609,9 +625,8 @@ var gameobject = {
|
||||||
this.clear();
|
this.clear();
|
||||||
this.objects = undefined;
|
this.objects = undefined;
|
||||||
|
|
||||||
|
if (typeof this.stop === 'function') this.stop();
|
||||||
if (typeof this.stop === 'function')
|
if (typeof this.die === 'function') this.die();
|
||||||
this.stop();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
up() { return [0,1].rotate(this.angle);},
|
up() { return [0,1].rotate(this.angle);},
|
||||||
|
@ -619,63 +634,10 @@ var gameobject = {
|
||||||
right() { return [1,0].rotate(this.angle);},
|
right() { return [1,0].rotate(this.angle);},
|
||||||
left() { return [-1,0].rotate(this.angle); },
|
left() { return [-1,0].rotate(this.angle); },
|
||||||
|
|
||||||
make() {
|
|
||||||
var obj = Object.create(this);
|
|
||||||
|
|
||||||
obj.make = undefined;
|
|
||||||
Object.mixin(obj,gameobject_impl);
|
|
||||||
|
|
||||||
obj.body = make_gameobject();
|
|
||||||
|
|
||||||
obj.components = {};
|
|
||||||
obj.objects = {};
|
|
||||||
obj.timers = [];
|
|
||||||
|
|
||||||
obj._ed = {
|
|
||||||
selectable: true,
|
|
||||||
dirty: false,
|
|
||||||
inst: false,
|
|
||||||
urdiff: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
obj.ur = this.toString();
|
|
||||||
obj.level = undefined;
|
|
||||||
|
|
||||||
obj.reparent(level);
|
|
||||||
|
|
||||||
cmd(113, obj.body, obj); // set the internal obj reference to this obj
|
|
||||||
|
|
||||||
for (var [prop,p] of Object.entries(this)) {
|
|
||||||
if (!p) continue;
|
|
||||||
if (component.isComponent(p)) {
|
|
||||||
obj[prop] = p.make(obj);
|
|
||||||
obj.components[prop] = obj[prop];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.hide(obj, 'ur','body', 'components', 'objects', '_ed', 'level', 'timers');
|
|
||||||
|
|
||||||
if (this.objects)
|
|
||||||
obj.make_objs(this.objects)
|
|
||||||
|
|
||||||
Object.dainty_assign(obj, this);
|
|
||||||
obj.sync();
|
|
||||||
gameobject.check_registers(obj);
|
|
||||||
|
|
||||||
if (data)
|
|
||||||
Object.dainty_assign(obj,data);
|
|
||||||
|
|
||||||
if (typeof obj.load === 'function') obj.load();
|
|
||||||
if (Game.playing() && typeof obj.start === 'function') obj.start();
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
|
|
||||||
make_objs(objs) {
|
make_objs(objs) {
|
||||||
for (var prop in objs) {
|
for (var prop in objs) {
|
||||||
var newobj = this.spawn_from_instance(objs[prop]);
|
say(`spawning ${json.encode(objs[prop])}`);
|
||||||
if (!newobj) continue;
|
var newobj = this.spawn(objs[prop]);
|
||||||
this.rename_obj(newobj.toString(), prop);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -700,10 +662,11 @@ var gameobject = {
|
||||||
return this.objects[newname];
|
return this.objects[newname];
|
||||||
},
|
},
|
||||||
|
|
||||||
add_component(comp, data) {
|
add_component(comp, data, name) {
|
||||||
data ??= undefined;
|
data ??= undefined;
|
||||||
if (typeof comp.make !== 'function') return;
|
if (typeof comp.make !== 'function') return;
|
||||||
var name = prosperon.obj_unique_name(comp.toString(), this);
|
name ??= comp.toString();
|
||||||
|
name = obj_unique_name(name, this);
|
||||||
this[name] = comp.make(this);
|
this[name] = comp.make(this);
|
||||||
this[name].comp = comp.toString();
|
this[name].comp = comp.toString();
|
||||||
this.components[name] = this[name];
|
this.components[name] = this[name];
|
||||||
|
@ -724,11 +687,11 @@ gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the
|
||||||
|
|
||||||
gameobject.doc = {
|
gameobject.doc = {
|
||||||
doc: "All objects in the game created through spawning have these attributes.",
|
doc: "All objects in the game created through spawning have these attributes.",
|
||||||
pos: "Position of the object, relative to its level.",
|
pos: "Position of the object, relative to its master.",
|
||||||
angle: "Rotation of this object, relative to its level.",
|
angle: "Rotation of this object, relative to its master.",
|
||||||
velocity: "Velocity of the object, relative to world.",
|
velocity: "Velocity of the object, relative to world.",
|
||||||
angularvelocity: "Angular velocity of the object, relative to the world.",
|
angularvelocity: "Angular velocity of the object, relative to the world.",
|
||||||
scale: "Scale of the object, relative to its level.",
|
scale: "Scale of the object, relative to its master.",
|
||||||
flipx: "Check if the object is flipped on its x axis.",
|
flipx: "Check if the object is flipped on its x axis.",
|
||||||
flipy: "Check if the object is flipped on its y axis.",
|
flipy: "Check if the object is flipped on its y axis.",
|
||||||
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`,
|
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`,
|
||||||
|
@ -757,7 +720,7 @@ gameobject.doc = {
|
||||||
dup: `Make an exact copy of this object.`,
|
dup: `Make an exact copy of this object.`,
|
||||||
transform: `Return an object representing the transform state of this object.`,
|
transform: `Return an object representing the transform state of this object.`,
|
||||||
kill: `Remove this object from the world.`,
|
kill: `Remove this object from the world.`,
|
||||||
level: "The entity this entity belongs to.",
|
master: "The entity this entity belongs to.",
|
||||||
delay: 'Run the given function after the given number of seconds has elapsed.',
|
delay: 'Run the given function after the given number of seconds has elapsed.',
|
||||||
cry: 'Make a sound. Can only make one at a time.',
|
cry: 'Make a sound. Can only make one at a time.',
|
||||||
add_component: 'Add a component to the object by name.',
|
add_component: 'Add a component to the object by name.',
|
||||||
|
@ -773,167 +736,7 @@ gameobject.doc = {
|
||||||
motor: 'Keeps the relative angular velocity of this body to to at a constant rate. The most simple idea is for one of the bodies to be static, to the other is kept at rate.'
|
motor: 'Keeps the relative angular velocity of this body to to at a constant rate. The most simple idea is for one of the bodies to be static, to the other is kept at rate.'
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Default objects */
|
var resavi = function(ur, path)
|
||||||
var prototypes = {};
|
|
||||||
prototypes.ur_ext = ".jso";
|
|
||||||
prototypes.ur = {};
|
|
||||||
|
|
||||||
/* Makes a new ur-type from disk. If the ur doesn't exist, it searches on the disk to create it. */
|
|
||||||
prototypes.from_file = function(file)
|
|
||||||
{
|
|
||||||
var urpath = file;
|
|
||||||
var path = urpath.split('.');
|
|
||||||
if (path.length > 1 && (path.at(-1) === path.at(-2))) {
|
|
||||||
urpath = path.slice(0,-1).join('.');
|
|
||||||
return prototypes.get_ur(urpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
var upperur = gameobject;
|
|
||||||
|
|
||||||
if (path.length > 1) {
|
|
||||||
var upur = undefined;
|
|
||||||
var upperpath = path.slice(0,-1);
|
|
||||||
while (!upur && upperpath) {
|
|
||||||
upur = prototypes.get_ur(upperpath.join('/'));
|
|
||||||
upperpath = upperpath.slice(0,-1);
|
|
||||||
}
|
|
||||||
if (upur) upperur = upur;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newur = {};
|
|
||||||
|
|
||||||
file = file.replaceAll('.','/');
|
|
||||||
|
|
||||||
var jsfile = prototypes.get_ur_file(urpath, prototypes.ur_ext);
|
|
||||||
var jsonfile = prototypes.get_ur_file(urpath, ".json");
|
|
||||||
|
|
||||||
var script = undefined;
|
|
||||||
var json = undefined;
|
|
||||||
|
|
||||||
if (jsfile) script = io.slurp(jsfile);
|
|
||||||
try {
|
|
||||||
if (jsonfile) json = JSON.parse(io.slurp(jsonfile));
|
|
||||||
} catch(e) {
|
|
||||||
console.warn(`Unable to create json from ${jsonfile}. ${e}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!json && !jsfile) {
|
|
||||||
console.warn(`Could not make ur from ${file}`);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script)
|
|
||||||
load_env(jsfile, newur);
|
|
||||||
|
|
||||||
json ??= {};
|
|
||||||
Object.merge(newur,json);
|
|
||||||
|
|
||||||
Object.entries(newur).forEach(function([k,v]) {
|
|
||||||
if (Object.isObject(v) && Object.isObject(upperur[k]))
|
|
||||||
v.__proto__ = upperur[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.values(newur).forEach(function(v) {
|
|
||||||
if (typeof v !== 'object') return;
|
|
||||||
if (!v.comp) return;
|
|
||||||
v.__proto__ = component[v.comp];
|
|
||||||
});
|
|
||||||
|
|
||||||
newur.__proto__ = upperur;
|
|
||||||
newur.instances = [];
|
|
||||||
Object.hide(newur, 'instances');
|
|
||||||
|
|
||||||
prototypes.list.push(urpath);
|
|
||||||
newur.toString = function() { return urpath; };
|
|
||||||
ur[urpath] = newur;
|
|
||||||
|
|
||||||
return newur;
|
|
||||||
}
|
|
||||||
prototypes.from_file.doc = "Create a new ur-type from a given script file.";
|
|
||||||
prototypes.list = [];
|
|
||||||
|
|
||||||
prototypes.list_ur = function()
|
|
||||||
{
|
|
||||||
var list = [];
|
|
||||||
function list_obj(obj, prefix)
|
|
||||||
{
|
|
||||||
prefix ??= "";
|
|
||||||
var list = [];
|
|
||||||
for (var e in obj) {
|
|
||||||
list.push(prefix + e);
|
|
||||||
list.concat(list_obj(obj[e], e + "."));
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list_obj(ur);
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.ur2file = function(urpath)
|
|
||||||
{
|
|
||||||
return urpath.replaceAll('.', '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.file2ur = function(file)
|
|
||||||
{
|
|
||||||
file = file.strip_ext();
|
|
||||||
file = file.replaceAll('/','.');
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.get_ur = function(name)
|
|
||||||
{
|
|
||||||
if (!name) return;
|
|
||||||
if (!name) {
|
|
||||||
console.error(`Can't get ur from ${name}.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var urpath = name;
|
|
||||||
if (urpath.includes('/'))
|
|
||||||
urpath = prototypes.file2ur(name);
|
|
||||||
|
|
||||||
if (!prototypes.ur[urpath]) {
|
|
||||||
var ur = prototypes.from_file(urpath);
|
|
||||||
if (ur)
|
|
||||||
return ur;
|
|
||||||
else {
|
|
||||||
console.warn(`Could not find prototype using name ${name}.`);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return prototypes.ur[urpath];
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.get_ur.doc = `Returns an ur, or makes it, for any given type of path
|
|
||||||
could be a file on a disk like ball/big.js
|
|
||||||
could be an ur path like ball.big`;
|
|
||||||
|
|
||||||
prototypes.get_ur_file = function(path, ext)
|
|
||||||
{
|
|
||||||
var urpath = prototypes.ur2file(path);
|
|
||||||
var file = urpath + ext;
|
|
||||||
if (io.exists(file)) return file;
|
|
||||||
file = urpath + "/" + path.split('.').at(-1) + ext;
|
|
||||||
if (io.exists(file)) return file;
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.generate_ur = function(path)
|
|
||||||
{
|
|
||||||
var ob = io.glob("**" + prototypes.ur_ext);
|
|
||||||
ob = ob.concat(io.glob("**.json"));
|
|
||||||
|
|
||||||
ob = ob.map(function(path) { return path.set_ext(""); });
|
|
||||||
ob = ob.map(function(path) { return path[0] !== '.' ? path : undefined; });
|
|
||||||
ob = ob.map(function(path) { return path[0] !== '_' ? path : undefined; });
|
|
||||||
ob = ob.filter(x => x !== undefined);
|
|
||||||
ob.forEach(function(name) { prototypes.get_ur(name); });
|
|
||||||
}
|
|
||||||
|
|
||||||
var ur = prototypes.ur;
|
|
||||||
|
|
||||||
prototypes.resavi = function(ur, path)
|
|
||||||
{
|
{
|
||||||
if (!ur) return path;
|
if (!ur) return path;
|
||||||
if (path[0] === '/') return path;
|
if (path[0] === '/') return path;
|
||||||
|
@ -946,7 +749,7 @@ prototypes.resavi = function(ur, path)
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
prototypes.resani = function(ur, path)
|
var resani = function(ur, path)
|
||||||
{
|
{
|
||||||
if (!path) return "";
|
if (!path) return "";
|
||||||
if (!ur) return path;
|
if (!ur) return path;
|
||||||
|
@ -964,57 +767,88 @@ prototypes.resani = function(ur, path)
|
||||||
return restry;
|
return restry;
|
||||||
}
|
}
|
||||||
|
|
||||||
prototypes.ur_dir = function(ur)
|
var ur = {};
|
||||||
{
|
ur._list = [];
|
||||||
var path = ur.replaceAll('.', '/');
|
|
||||||
console.warn(path);
|
/* UR OBJECT
|
||||||
console.warn(io.exists(path));
|
ur {
|
||||||
console.warn(`${path} does not exist; sending ${path.dir()}`);
|
name: fully qualified name of ur
|
||||||
|
text: file path to the script
|
||||||
|
data: file path to data
|
||||||
|
proto: resultant object of a freshly made entity
|
||||||
}
|
}
|
||||||
|
|
||||||
prototypes.ur_json = function(ur)
|
|
||||||
{
|
|
||||||
var path = ur.replaceAll('.', '/');
|
|
||||||
if (io.exists(path))
|
|
||||||
path = path + "/" + path.name() + ".json";
|
|
||||||
else
|
|
||||||
path = path + ".json";
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.ur_stem = function(ur)
|
|
||||||
{
|
|
||||||
var path = ur.replaceAll('.', '/');
|
|
||||||
if (io.exists(path))
|
|
||||||
return path + "/" + path.name();
|
|
||||||
else
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.ur_file_exts = ['.jso', '.json'];
|
|
||||||
|
|
||||||
prototypes.ur_folder = function(ur)
|
|
||||||
{
|
|
||||||
var path = ur.replaceAll('.', '/');
|
|
||||||
return io.exists(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.ur_pullout_folder = function(ur)
|
|
||||||
{
|
|
||||||
if (!prototypes.ur_folder(ur)) return;
|
|
||||||
|
|
||||||
var stem = prototypes.ur_stem(ur);
|
|
||||||
|
|
||||||
/* prototypes.ur_file_exts.forEach(function(e) {
|
|
||||||
var p = stem + e;
|
|
||||||
if (io.exists(p))
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Apply an ur u to an entity e */
|
||||||
|
/* u is given as */
|
||||||
|
function apply_ur(u, e)
|
||||||
|
{
|
||||||
|
if (typeof u !== 'string') {
|
||||||
|
console.warn("Must give u as a string.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var urs = u.split('.');
|
||||||
|
var config = {};
|
||||||
|
var topur = ur;
|
||||||
|
for (var i = 0; i < urs.length; i++) {
|
||||||
|
topur = topur[urs[i]];
|
||||||
|
if (!topur) {
|
||||||
|
console.warn(`Ur given by ${u} does not exist. Stopped at ${urs[i]}.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topur.text)
|
||||||
|
feval_env(topur.text, e);
|
||||||
|
|
||||||
|
if (topur.data)
|
||||||
|
Object.merge(config, json.decode(io.slurp(topur.data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.merge(e, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
function file2fqn(file)
|
||||||
|
{
|
||||||
|
var fqn = file.strip_ext();
|
||||||
|
if (fqn.folder_same_name())
|
||||||
|
fqn = fqn.up_path();
|
||||||
|
|
||||||
|
fqn = fqn.replace('/','.');
|
||||||
|
var topur;
|
||||||
|
if (topur = Object.access(ur,fqn)) return topur;
|
||||||
|
|
||||||
|
var fqnlast = fqn.split('.').last();
|
||||||
|
|
||||||
|
if (topur = Object.access(ur,fqn.tolast('.'))) {
|
||||||
|
topur[fqnlast] = {
|
||||||
|
name: fqn
|
||||||
|
};
|
||||||
|
ur._list.push(fqn);
|
||||||
|
return Object.access(ur,fqn);
|
||||||
|
}
|
||||||
|
|
||||||
|
fqn = fqnlast;
|
||||||
|
|
||||||
|
ur[fqn] = {
|
||||||
|
name: fqn
|
||||||
|
};
|
||||||
|
ur._list.push(fqn);
|
||||||
|
return ur[fqn];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIND ALL URS IN A PROJECT */
|
||||||
|
for (var file of io.glob("**.jso")) {
|
||||||
|
var topur = file2fqn(file);
|
||||||
|
topur.text = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var file of io.glob("**.json")) {
|
||||||
|
var topur = file2fqn(file);
|
||||||
|
topur.data = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gameobject,
|
gameobject,
|
||||||
actor,
|
|
||||||
prototypes,
|
|
||||||
ur
|
ur
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,6 +203,7 @@ var Player = {
|
||||||
n.pawns = [];
|
n.pawns = [];
|
||||||
n.gamepads = [];
|
n.gamepads = [];
|
||||||
this.players.push(n);
|
this.players.push(n);
|
||||||
|
this[this.players.length-1] = n;
|
||||||
return n;
|
return n;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -227,9 +228,12 @@ Player.print_pawns.doc = "Print out a list of the current pawn control stack.";
|
||||||
Player.doc = {};
|
Player.doc = {};
|
||||||
Player.doc.players = "A list of current players.";
|
Player.doc.players = "A list of current players.";
|
||||||
|
|
||||||
|
var player = Player;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Mouse,
|
Mouse,
|
||||||
Keys,
|
Keys,
|
||||||
input,
|
input,
|
||||||
Player,
|
Player,
|
||||||
|
player
|
||||||
};
|
};
|
||||||
|
|
119
scripts/render.js
Normal file
119
scripts/render.js
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
var render = {
|
||||||
|
normal() { cmd(67);},
|
||||||
|
wireframe() { cmd(68); },
|
||||||
|
pass() { },
|
||||||
|
};
|
||||||
|
|
||||||
|
render.doc = {
|
||||||
|
doc: "Functions for rendering modes.",
|
||||||
|
normal: "Final render with all lighting.",
|
||||||
|
wireframe: "Show only wireframes of models."
|
||||||
|
};
|
||||||
|
|
||||||
|
render.device = {
|
||||||
|
pc: [1920,1080],
|
||||||
|
macbook_m2: [2560,1664, 13.6],
|
||||||
|
ds_top: [400,240, 3.53],
|
||||||
|
ds_bottom: [320,240, 3.02],
|
||||||
|
playdate: [400,240,2.7],
|
||||||
|
switch: [1280,720, 6.2],
|
||||||
|
switch_lite: [1280,720,5.5],
|
||||||
|
switch_oled: [1280,720,7],
|
||||||
|
dsi: [256,192,3.268],
|
||||||
|
ds: [256,192, 3],
|
||||||
|
dsixl: [256,192,4.2],
|
||||||
|
ipad_air_m2: [2360,1640, 11.97],
|
||||||
|
iphone_se: [1334, 750, 4.7],
|
||||||
|
iphone_12_pro: [2532,1170,6.06],
|
||||||
|
iphone_15: [2556,1179,6.1],
|
||||||
|
gba: [240,160,2.9],
|
||||||
|
gameboy: [160,144,2.48],
|
||||||
|
gbc: [160,144,2.28],
|
||||||
|
steamdeck: [1280,800,7],
|
||||||
|
vita: [960,544,5],
|
||||||
|
psp: [480,272,4.3],
|
||||||
|
imac_m3: [4480,2520,23.5],
|
||||||
|
macbook_pro_m3: [3024,1964, 14.2],
|
||||||
|
ps1: [320,240,5],
|
||||||
|
ps2: [640,480],
|
||||||
|
snes: [256,224],
|
||||||
|
gamecube: [640,480],
|
||||||
|
n64: [320,240],
|
||||||
|
c64: [320,200],
|
||||||
|
macintosh: [512,342,9],
|
||||||
|
gamegear: [160,144,3.2],
|
||||||
|
};
|
||||||
|
|
||||||
|
render.device.doc = `Device resolutions given as [x,y,inches diagonal].`;
|
||||||
|
|
||||||
|
/* All draw in screen space */
|
||||||
|
render.point = function(pos,size,color) {
|
||||||
|
color ??= Color.blue;
|
||||||
|
render.circle(pos,size,color);
|
||||||
|
};
|
||||||
|
|
||||||
|
render.line = function(points, color, thickness) {
|
||||||
|
thickness ??= 1;
|
||||||
|
color ??= Color.white;
|
||||||
|
cmd(83, points, color, thickness);
|
||||||
|
};
|
||||||
|
|
||||||
|
render.poly = function(points, color) { cmd_points(0,points,color); };
|
||||||
|
|
||||||
|
render.circle = function(pos, radius, color) { cmd(115, pos, radius, color); };
|
||||||
|
|
||||||
|
render.cross = function(pos, size, color) {
|
||||||
|
color ??= Color.red;
|
||||||
|
var a = [
|
||||||
|
pos.add([0,size]),
|
||||||
|
pos.add([0,-size])
|
||||||
|
];
|
||||||
|
var b = [
|
||||||
|
pos.add([size,0]),
|
||||||
|
pos.add([-size,0])
|
||||||
|
];
|
||||||
|
|
||||||
|
render.line(a,color);
|
||||||
|
render.line(b,color);
|
||||||
|
};
|
||||||
|
|
||||||
|
render.arrow = function(start, end, color, wingspan, wingangle) {
|
||||||
|
color ??= Color.red;
|
||||||
|
wingspan ??= 4;
|
||||||
|
wingangle ??=10;
|
||||||
|
|
||||||
|
var dir = end.sub(start).normalized();
|
||||||
|
var wing1 = [
|
||||||
|
Vector.rotate(dir, wingangle).scale(wingspan).add(end),
|
||||||
|
end
|
||||||
|
];
|
||||||
|
var wing2 = [
|
||||||
|
Vector.rotate(dir,-wingangle).scale(wingspan).add(end),
|
||||||
|
end
|
||||||
|
];
|
||||||
|
render.line([start,end],color);
|
||||||
|
render.line(wing1,color);
|
||||||
|
render.line(wing2,color);
|
||||||
|
};
|
||||||
|
|
||||||
|
render.rectangle = function(lowerleft, upperright, color) {
|
||||||
|
var pos = lowerleft.add(upperright).map(x=>x/2);
|
||||||
|
var wh = [upperright.x-lowerleft.x,upperright.y-lowerleft.y];
|
||||||
|
render.box(pos,wh,color);
|
||||||
|
};
|
||||||
|
|
||||||
|
render.box = function(pos, wh, color) {
|
||||||
|
color ??= Color.white;
|
||||||
|
cmd(53, pos, wh, color);
|
||||||
|
};
|
||||||
|
|
||||||
|
render.doc = "Draw shapes in screen space.";
|
||||||
|
render.circle.doc = "Draw a circle at pos, with a given radius and color.";
|
||||||
|
render.cross.doc = "Draw a cross centered at pos, with arm length size.";
|
||||||
|
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
|
||||||
|
render.poly.doc = "Draw a concave polygon from a set of points.";
|
||||||
|
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
|
||||||
|
render.box.doc = "Draw a box centered at pos, with width and height in the tuple wh.";
|
||||||
|
render.line.doc = "Draw a line from a set of points, and a given thickness.";
|
||||||
|
|
||||||
|
return {render};
|
|
@ -113,3 +113,5 @@ Object.mixin(cmd(180).__proto__, {
|
||||||
pct() { return this.time()/this.length(); },
|
pct() { return this.time()/this.length(); },
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
return {audio};
|
||||||
|
|
112
scripts/spline.js
Normal file
112
scripts/spline.js
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
var Spline = {};
|
||||||
|
Spline.sample_angle = function(type, points, angle) {
|
||||||
|
return spline_cmd(0, type, points[0].length, points, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.bezier_loop = function(cp)
|
||||||
|
{
|
||||||
|
cp.push(Vector.reflect_point(cp.at(-2),cp.at(-1)));
|
||||||
|
cp.push(Vector.reflect_point(cp[1],cp[0]));
|
||||||
|
cp.push(cp[0].slice());
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.bezier_node_count = function(cp)
|
||||||
|
{
|
||||||
|
if (cp.length === 4) return 2;
|
||||||
|
return 2 + (cp.length-4)/3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.is_bezier = function(t) { return t === Spline.type.bezier; }
|
||||||
|
Spline.is_catmull = function(t) { return t === Spline.type.catmull; }
|
||||||
|
|
||||||
|
Spline.bezier2catmull = function(b)
|
||||||
|
{
|
||||||
|
var c = [];
|
||||||
|
for (var i = 0; i < b.length; i += 3)
|
||||||
|
c.push(b[i]);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.catmull2bezier = function(c)
|
||||||
|
{
|
||||||
|
var b = [];
|
||||||
|
for (var i = 1; i < c.length-2; i++) {
|
||||||
|
b.push(c[i].slice());
|
||||||
|
b.push(c[i+1].sub(c[i-1]).scale(0.25).add(c[i]));
|
||||||
|
b.push(c[i].sub(c[i+2]).scale(0.25).add(c[i+1]));
|
||||||
|
}
|
||||||
|
b.push(c[c.length-2]);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.catmull_loop = function(cp)
|
||||||
|
{
|
||||||
|
cp = cp.slice();
|
||||||
|
cp.unshift(cp.last());
|
||||||
|
cp.push(cp[1]);
|
||||||
|
cp.push(cp[2]);
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.catmull_caps = function(cp)
|
||||||
|
{
|
||||||
|
cp = cp.slice();
|
||||||
|
cp.unshift(cp[0].sub(cp[1]).add(cp[0]));
|
||||||
|
cp.push(cp.last().sub(cp.at(-2).add(cp.last())));
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.catmull2bezier.doc = "Given a set of control points C for a camtull-rom type curve, return a set of cubic bezier points to give the same curve."
|
||||||
|
|
||||||
|
Spline.type = {
|
||||||
|
catmull: 0,
|
||||||
|
bezier: 1,
|
||||||
|
bspline: 2,
|
||||||
|
cubichermite: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
Spline.bezier_tan_partner = function(points, i)
|
||||||
|
{
|
||||||
|
if (i%3 === 0) return undefined;
|
||||||
|
var partner_i = (i%3) === 2 ? i-1 : i+1;
|
||||||
|
return points[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.bezier_cp_mirror = function(points, i)
|
||||||
|
{
|
||||||
|
if (i%3 === 0) return undefined;
|
||||||
|
var partner_i = (i%3) === 2 ? i+2 : i-2;
|
||||||
|
var node_i = (i%3) === 2 ? i+1 : i-1;
|
||||||
|
if (partner_i >= points.length || node_i >= points.length) return;
|
||||||
|
points[partner_i] = points[node_i].sub(points[i]).add(points[node_i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.bezier_point_handles = function(points, i)
|
||||||
|
{
|
||||||
|
if (!Spline.bezier_is_node(points,i)) return [];
|
||||||
|
var a = i-1;
|
||||||
|
var b = i+1;
|
||||||
|
var c = []
|
||||||
|
if (a > 0)
|
||||||
|
c.push(a);
|
||||||
|
|
||||||
|
if (b < points.length)
|
||||||
|
c.push(b);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.bezier_nodes = function(points)
|
||||||
|
{
|
||||||
|
var c = [];
|
||||||
|
for (var i = 0; i < points.length; i+=3)
|
||||||
|
c.push(points[i].slice());
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spline.bezier_is_node = function(points, i) { return i%3 === 0; }
|
||||||
|
Spline.bezier_is_handle = function(points, i) { return !Spline.bezier_is_node(points,i); }
|
||||||
|
|
||||||
|
return {Spline};
|
|
@ -238,6 +238,10 @@ Cmdline.register_order("init", function() {
|
||||||
|
|
||||||
}, "Turn the directory into a Prosperon game.");
|
}, "Turn the directory into a Prosperon game.");
|
||||||
|
|
||||||
|
Cmdline.register_order("debug", function() {
|
||||||
|
Cmdline.orders.play();
|
||||||
|
}, "Play the game with debugging enabled.");
|
||||||
|
|
||||||
Cmdline.register_order("play", function() {
|
Cmdline.register_order("play", function() {
|
||||||
if (!io.exists(".prosperon/project")) {
|
if (!io.exists(".prosperon/project")) {
|
||||||
say("No game to play. Try making one with 'prosperon init'.");
|
say("No game to play. Try making one with 'prosperon init'.");
|
||||||
|
@ -247,8 +251,8 @@ Cmdline.register_order("play", function() {
|
||||||
var project = json.decode(io.slurp(".prosperon/project"));
|
var project = json.decode(io.slurp(".prosperon/project"));
|
||||||
|
|
||||||
Game.engine_start(function() {
|
Game.engine_start(function() {
|
||||||
load("config.js");
|
global.mixin("config.js");
|
||||||
load("game.js");
|
global.mixin("game.js");
|
||||||
if (project.icon) Window.icon(project.icon);
|
if (project.icon) Window.icon(project.icon);
|
||||||
if (project.title) Window.title(project.title);
|
if (project.title) Window.title(project.title);
|
||||||
});
|
});
|
||||||
|
@ -379,35 +383,6 @@ function cmd_args(cmdargs)
|
||||||
|
|
||||||
Cmdline.register_order("clean", function(argv) {
|
Cmdline.register_order("clean", function(argv) {
|
||||||
say("Cleaning not implemented.");
|
say("Cleaning not implemented.");
|
||||||
return;
|
|
||||||
|
|
||||||
var f = argv[0];
|
|
||||||
if (argv.length === 0) {
|
|
||||||
Cmdline.print_order("clean");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!io.exists(f)) {
|
|
||||||
say(`File ${f} does not exist.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
prototypes.generate_ur();
|
|
||||||
|
|
||||||
var j = json.decode(io.slurp(f));
|
|
||||||
|
|
||||||
for (var k in j)
|
|
||||||
if (k in j.objects)
|
|
||||||
delete j[k];
|
|
||||||
|
|
||||||
console.warn(j);
|
|
||||||
|
|
||||||
for (var k in j.objects) {
|
|
||||||
var o = j.objects[k];
|
|
||||||
samediff(o, ur[o.ur]);
|
|
||||||
}
|
|
||||||
|
|
||||||
say(j);
|
|
||||||
}, "Clean up a given object file.", "JSON ...");
|
}, "Clean up a given object file.", "JSON ...");
|
||||||
|
|
||||||
Cmdline.register_cmd("l", function(n) {
|
Cmdline.register_cmd("l", function(n) {
|
||||||
|
|
|
@ -196,3 +196,5 @@ var Tween = {
|
||||||
};
|
};
|
||||||
|
|
||||||
Tween.make = Tween.start;
|
Tween.make = Tween.start;
|
||||||
|
|
||||||
|
return {Tween, Ease};
|
||||||
|
|
|
@ -212,8 +212,9 @@ double js2number(JSValue v) {
|
||||||
|
|
||||||
void *js2ptr(JSValue v) { return JS_GetOpaque(v,js_ptr_id); }
|
void *js2ptr(JSValue v) { return JS_GetOpaque(v,js_ptr_id); }
|
||||||
|
|
||||||
JSValue float2js(double g) { return JS_NewFloat64(js, g);}
|
JSValue number2js(double g) {
|
||||||
JSValue number2js(double g) { return float2js(g); }
|
return JS_NewFloat64(js,g);
|
||||||
|
}
|
||||||
struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); }
|
struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); }
|
||||||
|
|
||||||
JSValue ptr2js(void *ptr) {
|
JSValue ptr2js(void *ptr) {
|
||||||
|
@ -1085,6 +1086,11 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
|
||||||
ret = number2js(get_timescale());
|
ret = number2js(get_timescale());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 122:
|
||||||
|
str = JS_ToCString(js, argv[1]);
|
||||||
|
ret = file_eval_env(str, argv[2]);
|
||||||
|
break;
|
||||||
|
|
||||||
case 123:
|
case 123:
|
||||||
str = JS_ToCString(js, argv[1]);
|
str = JS_ToCString(js, argv[1]);
|
||||||
str2 = JS_ToCString(js, argv[3]);
|
str2 = JS_ToCString(js, argv[3]);
|
||||||
|
@ -1137,7 +1143,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 135:
|
case 135:
|
||||||
ret = float2js(cam_zoom());
|
ret = number2js(cam_zoom());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 136:
|
case 136:
|
||||||
|
|
|
@ -117,7 +117,6 @@ char *nota_write_int(long long n, char *nota)
|
||||||
return nota_continue_num(n, nota, 3);
|
return nota_continue_num(n, nota, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define NOTA_DBL_PREC 6
|
#define NOTA_DBL_PREC 6
|
||||||
#define xstr(s) str(s)
|
#define xstr(s) str(s)
|
||||||
#define str(s) #s
|
#define str(s) #s
|
||||||
|
|
|
@ -11,9 +11,10 @@
|
||||||
|
|
||||||
#define NOTA_FALSE 0x00
|
#define NOTA_FALSE 0x00
|
||||||
#define NOTA_TRUE 0x01
|
#define NOTA_TRUE 0x01
|
||||||
|
#define NOTA_NULL 0x02
|
||||||
|
#define NOTA_INF 0x03
|
||||||
#define NOTA_PRIVATE 0x08
|
#define NOTA_PRIVATE 0x08
|
||||||
#define NOTA_SYSTEM 0x09
|
#define NOTA_SYSTEM 0x09
|
||||||
#define NOTA_NULL 0x02
|
|
||||||
|
|
||||||
typedef struct NOTA {
|
typedef struct NOTA {
|
||||||
char *head;
|
char *head;
|
||||||
|
|
|
@ -148,8 +148,6 @@ void *cdb_slurp(struct cdb *cdb, const char *file, size_t *size)
|
||||||
|
|
||||||
int fexists(const char *path)
|
int fexists(const char *path)
|
||||||
{
|
{
|
||||||
return !access(path,R_OK);
|
|
||||||
|
|
||||||
int len = strlen(path);
|
int len = strlen(path);
|
||||||
if (cdb_find(&game_cdb, path, len)) return 1;
|
if (cdb_find(&game_cdb, path, len)) return 1;
|
||||||
else if (cdb_find(&corecdb, path, len)) return 1;
|
else if (cdb_find(&corecdb, path, len)) return 1;
|
||||||
|
|
|
@ -231,7 +231,7 @@ JSValue eval_file_env(const char *script, const char *file, JSValue env)
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void file_eval_env(const char *file, JSValue env)
|
JSValue file_eval_env(const char *file, JSValue env)
|
||||||
{
|
{
|
||||||
size_t len;
|
size_t len;
|
||||||
char *script = slurp_text(file, &len);
|
char *script = slurp_text(file, &len);
|
||||||
|
|
|
@ -48,7 +48,7 @@ void call_callee(struct callee *c);
|
||||||
void script_callee(struct callee c, int argc, JSValue *argv);
|
void script_callee(struct callee c, int argc, JSValue *argv);
|
||||||
int script_has_sym(void *sym);
|
int script_has_sym(void *sym);
|
||||||
void script_eval_w_env(const char *s, JSValue env, const char *file);
|
void script_eval_w_env(const char *s, JSValue env, const char *file);
|
||||||
void file_eval_env(const char *file, JSValue env);
|
JSValue file_eval_env(const char *file, JSValue env);
|
||||||
|
|
||||||
time_t file_mod_secs(const char *file);
|
time_t file_mod_secs(const char *file);
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,6 @@
|
||||||
|
|
||||||
static warp_gravity **warps = NULL;
|
static warp_gravity **warps = NULL;
|
||||||
|
|
||||||
warp_gravity *warp_gravity_make()
|
|
||||||
{
|
|
||||||
warp_gravity *n = calloc(sizeof(*n),1);
|
|
||||||
n->strength = 9.8;
|
|
||||||
n->t.scale = (HMM_Vec3){0,-1,0};
|
|
||||||
n->planar_force = HMM_MulV3F(n->t.scale, n->strength);
|
|
||||||
arrput(warps, n);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
warp_damp *warp_damp_make()
|
warp_damp *warp_damp_make()
|
||||||
{
|
{
|
||||||
warp_damp *d = calloc(sizeof(*d),1);
|
warp_damp *d = calloc(sizeof(*d),1);
|
||||||
|
@ -21,6 +11,17 @@ warp_damp *warp_damp_make()
|
||||||
}
|
}
|
||||||
|
|
||||||
void warp_damp_free(warp_damp *d) { free(d); }
|
void warp_damp_free(warp_damp *d) { free(d); }
|
||||||
|
|
||||||
|
warp_gravity *warp_gravity_make()
|
||||||
|
{
|
||||||
|
warp_gravity *n = calloc(sizeof(*n),1);
|
||||||
|
n->strength = 9.8;
|
||||||
|
n->t.scale = (HMM_Vec3){0,-1,0};
|
||||||
|
n->planar_force = (HMM_Vec3){0,-1,0};
|
||||||
|
arrput(warps, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
void warp_gravity_free(warp_gravity *n) {
|
void warp_gravity_free(warp_gravity *n) {
|
||||||
for (int i = 0; i < arrlen(warps); i++) {
|
for (int i = 0; i < arrlen(warps); i++) {
|
||||||
if (warps[i] == n) {
|
if (warps[i] == n) {
|
||||||
|
@ -47,7 +48,7 @@ HMM_Vec3 warp_gravity_force(warp_gravity *g, HMM_Vec3 pos)
|
||||||
HMM_Vec3 norm = HMM_NormV3(HMM_SubV3(g->t.pos, pos));
|
HMM_Vec3 norm = HMM_NormV3(HMM_SubV3(g->t.pos, pos));
|
||||||
return HMM_MulV3F(norm,g->strength);
|
return HMM_MulV3F(norm,g->strength);
|
||||||
} else {
|
} else {
|
||||||
return g->planar_force;
|
return HMM_MulV3F(g->planar_force, g->strength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,5 +44,4 @@ warp_damp *warp_damp_make();
|
||||||
void warp_gravity_free(warp_gravity *g);
|
void warp_gravity_free(warp_gravity *g);
|
||||||
void warp_damp_free(warp_damp *d);
|
void warp_damp_free(warp_damp *d);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue