Update what's in global scope

This commit is contained in:
John Alanbrook 2024-02-25 23:31:48 +00:00
parent 9c8fe27ce4
commit 4351b4bf20
27 changed files with 634 additions and 777 deletions

View file

@ -41,14 +41,29 @@ Poke around the example projects. You will find it refreshingly straight forward
* Engine Tour
Prosperon is built in a code-first fashion.
** QuickJS (Javascript)
** Scripting
The scripting language used in Prosperon is Javascript, with QuickJS. It is [[https://tc39.es/ecma262/2023/][ES2023]] compliant. It is fast, and has a number of features that make it well suited for a video game engine, like ref counting. Read more about it [[https://bellard.org/quickjs/][here]].
#+begin_scholium
Javascript is used here mostly to set up objects and tear them down. Although computationally expensive tasks can be done using QuickJS, Prosperon makes it easy to extend with raw C.
#+end_scholium
** Script entry points
*** How Prosperon games are structued
Prosperon games are structured into two types of source files:
- scripts
- actors
When any source file is executed, it is executed in its own space. Global properties are accessible, but any declared variables and functions are not automatically added to the global space. To add obejcts to the global space, it must be explicitly done, by assigning them to ~global~ or ~globalThis~.
#+begin_src
global.hellofn = function() { say("Hello, world!"); }; // hellofn is now callable anywhere
#+end_src
When a *script* is loaded, its statements are executed in order, in the context of the containing source. The containing source is accessible via the ~$~ variable. When ~load~ is called, an optional object is permitted to be supplied as the ~$~. This makes writing scripts with mixin functionality simple.
The parameter ~$$~ is given to a persistent object that is available whenever a script is executing, per script.
*** Script entry points
The first way you can customize Prosperon is by adding scripts to the folder you're running it from. Any file ending with *.js* is a *script* which can be ran by Prosperon.
| script | When called |

View file

@ -1,4 +1,4 @@
var AI = {
var ai = {
race(list) {
return function(dt) {
var good = false;

View file

@ -22,56 +22,54 @@ Set = undefined;
WeakSet = undefined;
*/
var fmt = {};
var roman_numerals = {
Number.roman = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1
};
function roman2arabic(roman)
{
var num = 0;
for (var i = 0; i < roman.length; i++) {
var rm = roman_numerals[roman[i]];
if (i + 1 < roman.length && rm < roman_numerals[roman[i+1]])
num -= rm;
else
num += rm;
}
var convert = {};
convert.romanize = function(num) {
if (!+num) return false;
var digits = String(+num).split('');
var key = ['','C','CC','CCC','CD','D','DC','DCC','DCCC','CM',
'','X','XX','XXX','XL','L','LX','LXX','LXXX','XC',
'','I','II','III','IV','V','VI','VII','VIII','IX'];
var roman = '', i = 3;
while (i--) roman = (key[+digits.pop() + (i * 10)] || '') + roman;
return Array(+digits.join('') + 1).join('M') + roman;
}
convert.deromanize = function(str) {
var str = str.toUpperCase();
var validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/;
var token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g;
var key = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1};
var num = 0, m;
if (!(str && validator.test(str))) return false;
while (m = token.exec(str)) num += key[m[0]];
return num;
}
function arabic2roman(num)
{
if (num <= 0 || num >= 4000)
return "Invalid input. Roman numerals are not defined for numbers less than 1 or greater than 3999.";
var result = '';
for (var key in roman_numerals) {
while (num >= roman_numerals[key]) {
result += key;
num -= roman_numerals[key];
}
}
return result;
convert.buf2hex = function(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join(' ');
}
var timeparse = {
/* Time values are always expressed in terms of real earth-seconds */
var time = {
get hour2minute() { return this.hour/this.minute; },
get day2hour() { return this.day/this.hour; },
get minute2second() { return this.minute/this.second; },
get week2day() { return this.week/this.day; },
};
time.strparse = {
yyyy: "year",
mm: "month",
m: "month",
@ -87,29 +85,38 @@ var timeparse = {
s: "second",
};
String.parse = function(str, p)
{
var rec = {};
var fmts = Object.keys(p).sort(function(a,b) { return a.length > b.length; });
}
/* Time values are always expressed in terms of real earth-seconds */
var time = {
get hour2minute() { return this.hour/this.minute; },
get day2hour() { return this.day/this.hour; },
get minute2second() { return this.minute/this.second; },
get week2day() { return this.week/this.day; },
time.doc = {
doc: "Functions for manipulating time.",
second: "Earth-seconds in a second.",
minute: "Seconds in a minute.",
hour: "Seconds in an hour.",
day: "Seconds in a day.",
week: "Seconds in a week.",
weekdays: "Names of the days of the week.",
monthstr: "Full names of the months of the year.",
epoch: "Times are expressed in terms of day 0 at hms 0 of this year.",
now: "Get the time now.",
computer_zone: "Get the time zone of the running computer.",
computer_dst: "Return true if the computer is in daylight savings.",
yearsize: "Given a year, return the number of days in that year.",
monthdays: "Number of days in each month.",
fmt: "Default format for time.",
record: "Given a time, return an object with time fields.",
number: "Return the number representation of a given time.",
text: "Return a text formatted time."
};
time.second = 1; /* earth-seconds in a second */
time.minute = 60; /* seconds in a minute */
time.hour = 3_600; /* seconds in an hour */
time.day = 86_400; /* seconds in a day */
time.week = 604_800; /* seconds in a week */
time.second = 1;
time.minute = 60;
time.hour = 3_600;
time.day = 86_400;
time.week = 604_800;
time.weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
time.monthstr = ["January", "February", "March", 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
time.epoch = 1970; /* Times are expressed in terms of day 0 at hms 0 of this year */
time.epoch = 1970;
time.now = function() { return cmd(210);}
time.computer_zone = function() { return cmd(211)/this.hour; }
time.computer_dst = function() { return cmd(212); }
@ -121,7 +128,6 @@ time.yearsize = function(y) {
return 366;
return 365;
}
time.yearsize.doc = "Given a year, return the number of days in that year.";
time.monthdays = [31,28,31,30,31,30,31,31,30,31,30,31];
time.zones = {};
@ -329,6 +335,13 @@ json.readout = function(obj)
return json.encode(j);
}
json.doc = {
doc: "json implementation.",
encode: "Encode a value to json.",
decode: "Decode a json string to a value.",
readout: "Encode an object fully, including function definitions."
};
Object.methods = function(o)
{
var m = [];
@ -521,7 +534,7 @@ Object.copy = function(proto, ...objs)
return c;
}
/* OBJECT DEFININTIONS */
/* OBJECT DEFININTioNS */
Object.defHidden = function(obj, prop)
{
Object.defineProperty(obj, prop, {enumerable:false, writable:true});
@ -532,7 +545,7 @@ Object.hide = function(obj,...props)
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p) {
Log.info(`No property of name ${prop}.`);
console.info(`No property of name ${prop}.`);
continue;
}
p.enumerable = false;
@ -545,7 +558,7 @@ Object.unhide = function(obj, ...props)
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj,prop);
if (!p) {
Log.warn(`No property of name ${prop}.`);
console.warn(`No property of name ${prop}.`);
return;
}
p.enumerable = true;
@ -868,22 +881,6 @@ Object.defineProperty(Array.prototype, 'dofilter', {
}
});
function filterInPlace(a, condition, thisArg) {
let j = 0;
a.forEach((e, i) => {
if (condition.call(thisArg, e, i, a)) {
if (i!==j) a[j] = e;
j++;
}
});
a.length = j;
return a;
}
Object.defineProperty(Array.prototype, 'reversed', {
value: function() {
var c = this.slice();
@ -897,6 +894,7 @@ Object.defineProperty(Array.prototype, 'rotate', {
}
});
function make_swizz() {
function setelem(n) {
return {
get: function() { return this[n]; },
@ -969,7 +967,6 @@ swizz.forEach(function(x) {
});
});
swizz = [];
for (var i of nums)
for (var j of nums)
@ -995,7 +992,8 @@ swizz.forEach(function(x) {
});
});
};
make_swizz();
Object.defineProperty(Array.prototype, 'add', {
value: function(b) {
@ -1084,9 +1082,6 @@ value: function(b) {
return true;
}});
function add(x,y) { return x+y; };
function mult(x,y) { return x*y; };
Object.defineProperty(Array.prototype, 'mapc', {
value: function(fn) {
return this.map(x => fn(x));
@ -1285,7 +1280,7 @@ Object.defineProperty(Object.prototype, 'lerp',{
return obj;
}});
/* MATH EXTENSIONS */
/* MATH EXTENSioNS */
Object.defineProperty(Number.prototype, 'lerp', {
value: function(to, t) {
var s = this;
@ -1332,6 +1327,8 @@ Math.deg2turn = function(x) { return x/360; };
Math.randomint = function(max) { return Math.clamp(Math.floor(Math.random() * max), 0, max-1); };
/* BOUNDINGBOXES */
var bbox = {};
function cwh2bb(c, wh) {
return {
t: c.y+(wh.y/2),
@ -1518,7 +1515,7 @@ function points2cm(points)
return [x/n,y/n];
};
function sortpointsccw(points)
Math.sortpointsccw = function(points)
{
var cm = points2cm(points);
var cmpoints = points.map(function(x) { return x.sub(cm); });

View file

@ -20,19 +20,16 @@ Color.tohtml = function(v)
return "#" + html.join('');
}
Color.toesc = function(v)
{
return Esc.color(v);
}
var Esc = {};
Esc.reset = "\x1b[0";
Esc.color = function(v) {
var esc = {};
esc.reset = "\x1b[0";
esc.color = function(v) {
var c = v.map(function(n) { return Math.floor(n*255); });
var truecolor = "\x1b[38;2;" + c.join(';') + ';';
return truecolor;
}
esc.doc = "Functions and constants for ANSI escape sequences.";
Color.Arkanoid = {
orange: [255,143,0],
teal: [0,255,255],

View file

@ -1,29 +1,3 @@
function make_point_obj(o, p)
{
return {
pos: p,
move(d) {
d = o.gameobject.dir_world2this(d);
p.x += d.x;
p.y += d.y;
},
sync: o.sync.bind(o)
}
}
function assign_impl(obj, impl)
{
var tmp = {};
for (var key of Object.keys(impl))
if (typeof obj[key] !== 'undefined' && typeof obj[key] !== 'function')
tmp[key] = obj[key];
Object.mixin(obj, impl);
for (var key in tmp)
obj[key] = tmp[key];
}
var component = {
components: [],
toString() {
@ -57,7 +31,7 @@ var component = {
return nc;
},
kill() { Log.info("Kill not created for this component yet"); },
kill() { console.info("Kill not created for this component yet"); },
sync() {},
post(){},
gui() { },
@ -68,6 +42,33 @@ var component = {
extend(spec) { return Object.copy(this, spec); },
};
component.util = {};
component.util.make_point_obj = function(o, p)
{
return {
pos: p,
move(d) {
d = o.gameobject.dir_world2this(d);
p.x += d.x;
p.y += d.y;
},
sync: o.sync.bind(o)
}
}
component.util.assign_impl = function(obj, impl)
{
var tmp = {};
for (var key of Object.keys(impl))
if (typeof obj[key] !== 'undefined' && typeof obj[key] !== 'function')
tmp[key] = obj[key];
Object.mixin(obj, impl);
for (var key in tmp)
obj[key] = tmp[key];
}
component.sprite = Object.copy(component, {
pos:[0,0],
color:[1,1,1,1],
@ -301,7 +302,7 @@ var SpriteAnim = {
return anim;
};
var json = IO.slurp(path);
var json = io.slurp(path);
json = JSON.parse(json);
var anims = {};
var frames = Array.isArray(json.frames) ? json.frames : Object.values(json.frames);
@ -324,8 +325,8 @@ var SpriteAnim = {
},
find(path) {
if (!IO.exists(path + ".asset")) return;
var asset = JSON.parse(IO.slurp(path + ".asset"));
if (!io.exists(path + ".asset")) return;
var asset = JSON.parse(io.slurp(path + ".asset"));
},
};
@ -336,57 +337,6 @@ SpriteAnim.strip.doc = 'Given a path and number of frames, converts a horizontal
SpriteAnim.aseprite.doc = 'Given an aseprite json metadata, returns an object of animations defined in the aseprite file.';
SpriteAnim.find.doc = 'Given a path, find the relevant animation for the file.';
/* Returns points specifying this geometry, with ccw */
var Geometry = {
box(w, h) {
w /= 2;
h /= 2;
var points = [
[w,h],
[-w,h],
[-w,-h],
[w,-h]
];
return points;
},
arc(radius, angle, n, start) {
start ??= 0;
start = Math.deg2rad(start);
if (angle >= 360)
angle = 360;
if (n <= 1) return [];
var points = [];
angle = Math.deg2rad(angle);
var arclen = angle/n;
for (var i = 0; i < n; i++)
points.push(Vector.rotate([radius,0], start + (arclen*i)));
return points;
},
circle(radius, n) {
if (n <= 1) return [];
return Geometry.arc(radius, 360, n);
},
ngon(radius, n) {
return Geometry.arc(radius,360,n);
},
};
Geometry.doc = {
doc: "Functions for creating a list of points for various geometric shapes.",
box: "Create a box.",
arc: "Create an arc, made of n points.",
circle: "Create a circle, made of n points.",
ngon: "Create a polygon of n sides.",
};
/* For all colliders, "shape" is a pointer to a phys2d_shape, "id" is a pointer to the shape data */
var collider2d = Object.copy(component, {
name: "collider 2d",
@ -451,7 +401,7 @@ component.polygon2d = Object.copy(collider2d, {
},
gizmo() {
this.spoints().forEach(x => Shape.point(this.gameobject.this2screen(x), 3, Color.green));
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.green));
this.points.forEach((x,i)=>Debug.numbered_point(this.gameobject.this2screen(x), i));
},
@ -478,7 +428,7 @@ var polygon2d = component.polygon2d;
polygon2d.inputs = {};
//polygon2d.inputs.post = function() { this.sync(); };
polygon2d.inputs.f10 = function() {
this.points = sortpointsccw(this.points);
this.points = Math.sortpointsccw(this.points);
};
polygon2d.inputs.f10.doc = "Sort all points to be CCW order.";
@ -596,7 +546,7 @@ component.edge2d = Object.copy(collider2d, {
/* EDITOR */
gizmo() {
if (this.type === Spline.type.catmull || this.type === -1) {
this.spoints().forEach(x => Shape.point(this.gameobject.this2screen(x), 3, Color.teal));
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.teal));
this.cpoints.forEach((x,i) => Debug.numbered_point(this.gameobject.this2screen(x), i));
} else {
for (var i = 0; i < this.cpoints.length; i += 3)
@ -605,8 +555,8 @@ component.edge2d = Object.copy(collider2d, {
for (var i = 1; i < this.cpoints.length; i+=3) {
Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i]), i, Color.green);
Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i+1]), i+1, Color.green);
Shape.line([this.gameobject.this2screen(this.cpoints[i-1]), this.gameobject.this2screen(this.cpoints[i])], Color.yellow);
Shape.line([this.gameobject.this2screen(this.cpoints[i+1]), this.gameobject.this2screen(this.cpoints[i+2])], Color.yellow);
render.line([this.gameobject.this2screen(this.cpoints[i-1]), this.gameobject.this2screen(this.cpoints[i])], Color.yellow);
render.line([this.gameobject.this2screen(this.cpoints[i+1]), this.gameobject.this2screen(this.cpoints[i+2])], Color.yellow);
}
}
},

View file

@ -1,8 +1,8 @@
/* All draw in screen space */
var Shape = {
Object.assign(render, {
point(pos,size,color) {
color ??= Color.blue;
Shape.circle(pos,size,color);
render.circle(pos,size,color);
},
line(points, color, thickness) {
@ -27,8 +27,8 @@ var Shape = {
pos.add([-size,0])
];
Shape.line(a,color);
Shape.line(b,color);
render.line(a,color);
render.line(b,color);
},
arrow(start, end, color, wingspan, wingangle) {
@ -45,15 +45,15 @@ var Shape = {
Vector.rotate(dir,-wingangle).scale(wingspan).add(end),
end
];
Shape.line([start,end],color);
Shape.line(wing1,color);
Shape.line(wing2,color);
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];
Shape.box(pos,wh,color);
render.box(pos,wh,color);
},
box(pos, wh, color) {
@ -61,16 +61,16 @@ var Shape = {
cmd(53, pos, wh, color);
},
};
});
Shape.doc = "Draw shapes in screen space.";
Shape.circle.doc = "Draw a circle at pos, with a given radius and color.";
Shape.cross.doc = "Draw a cross centered at pos, with arm length size.";
Shape.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
Shape.poly.doc = "Draw a concave polygon from a set of points.";
Shape.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
Shape.box.doc = "Draw a box centered at pos, with width and height in the tuple wh.";
Shape.line.doc = "Draw a line from a set of points, and a given thickness.";
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 = {
fn_break(fn, obj) {
@ -98,7 +98,7 @@ var Debug = {
numbered_point(pos, n, color) {
color ??= Color.white;
Shape.point(pos, 3, color);
render.point(pos, 3, color);
GUI.text(n, pos.add([0,4]), 1, color);
},
@ -127,12 +127,12 @@ var Debug = {
if (this.draw_gizmos)
Game.all_objects(function(x) {
if (!x.icon) return;
GUI.image(x.icon, world2screen(x.pos));
GUI.image(x.icon, Window.world2screen(x.pos));
});
if (this.draw_names)
Game.all_objects(function(x) {
GUI.text(x, world2screen(x.pos).add([0,32]), 1, Color.Debug.names);
GUI.text(x, Window.world2screen(x.pos).add([0,32]), 1, Color.Debug.names);
});
if (Debug.Options.gif.rec) {
@ -173,7 +173,7 @@ var Gizmos = {
},
};
var Profile = {
Object.assign(profile, {
tick_now() { return cmd(127); },
ns(ticks) { return cmd(128, ticks); },
us(ticks) { return cmd(129, ticks); },
@ -197,23 +197,23 @@ var Profile = {
cpu(fn, times, q) {
times ??= 1;
q ??= "unnamed";
var start = Profile.tick_now();
var start = profile.tick_now();
for (var i = 0; i < times; i++)
fn();
var elapsed = Profile.tick_now() - start;
var avgt = Profile.best_t(elapsed/times);
var totalt = Profile.best_t(elapsed);
var elapsed = profile.tick_now() - start;
var avgt = profile.best_t(elapsed/times);
var totalt = profile.best_t(elapsed);
console.say(`Profile [${q}]: ${avgt.time.toFixed(3)} ${avgt.unit} average [${totalt.time.toFixed(3)} ${totalt.unit} for ${times} loops]`);
console.say(`profile [${q}]: ${avgt.time.toFixed(3)} ${avgt.unit} average [${totalt.time.toFixed(3)} ${totalt.unit} for ${times} loops]`);
},
get fps() { return sys_cmd(8); },
};
});
Profile.test = {
profile.test = {
barecall() { profile(0); },
unpack_num(n) { profile(1,n); },
unpack_array(n) { profile(2,n); },
@ -224,9 +224,9 @@ Profile.test = {
call_fn_n(fn1, n) { profile(7,fn1,n,fn2); },
};
Profile.test.call_fn_n.doc = "Calls fn1 n times, and then fn2.";
profile.test.call_fn_n.doc = "Calls fn1 n times, and then fn2.";
Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`;
profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`;
/* These controls are available during editing, and during play of debug builds */
var DebugControls = {};
@ -292,13 +292,11 @@ DebugControls.inputs.f9 = function() {
DebugControls.inputs.f10 = function() { Time.timescale = 0.1; };
DebugControls.inputs.f10.doc = "Toggle timescale to 1/10.";
DebugControls.inputs.f10.released = function () { Time.timescale = 1.0; };
DebugControls.inputs.f12 = function() { GUI.defaults.debug = !GUI.defaults.debug; Log.warn("GUI toggle debug");};
DebugControls.inputs.f12 = function() { GUI.defaults.debug = !GUI.defaults.debug; console.warn("GUI toggle debug");};
DebugControls.inputs.f12.doc = "Toggle drawing GUI debugging aids.";
DebugControls.inputs['M-1'] = Render.normal;
Render.normal.doc = "Render mode for enabling all shaders and lighting effects.";
DebugControls.inputs['M-2'] = Render.wireframe;
Render.wireframe.doc = "Render mode to see wireframes of all models.";
DebugControls.inputs['M-1'] = render.normal;
DebugControls.inputs['M-2'] = render.wireframe;
DebugControls.inputs['C-M-f'] = function() {};
DebugControls.inputs['C-M-f'].doc = "Enter camera fly mode.";
@ -327,7 +325,7 @@ var Time = {
play() {
if (!Time.stash) {
Log.warn("Tried to resume time without calling Time.pause first.");
console.warn("Tried to resume time without calling Time.pause first.");
return;
}
Time.timescale = Time.stash;
@ -346,40 +344,8 @@ Time.doc.play = "Resume the game after using Time.pause.";
Player.players[0].control(DebugControls);
Register.gui.register(Debug.draw, Debug);
var console = Object.create(Log);
console.log = function(str)
{
console.say(time.text(time.now(), 'yyyy-m-dd hh:nn:ss') + " " + str);
}
console.clear = function()
{
cmd(146);
}
console.assert = function(assertion, msg, objs)
{
if (!assertion) {
console.error(msg);
console.stack();
}
}
var say = function(msg) {
console.say(msg);
}
say.doc = "Print to std out with an appended newline.";
var gist = function(o)
{
if (typeof o === 'object') return json.encode(o,null,1);
if (typeof o === 'string') return o;
return o.toString();
}
gist.doc = "Return the best string gist of an object.";
var API = {};
API.doc_entry = function(obj, key)
Debug.api = {};
Debug.api.doc_entry = function(obj, key)
{
if (typeof key !== 'string') {
console.warn("Cannot print a key that isn't a string.");
@ -415,7 +381,7 @@ ${doc}
`;
}
API.print_doc = function(name)
Debug.api.print_doc = function(name)
{
var obj = name;
if (typeof name === 'string') {
@ -448,7 +414,7 @@ API.print_doc = function(name)
if (key === 'doc') continue;
if (key === 'toString') continue;
mdoc += API.doc_entry(obj, key) + "\n";
mdoc += Debug.api.doc_entry(obj, key) + "\n";
}
return mdoc;

View file

@ -26,14 +26,14 @@ function unmerge(target, source) {
/* Deeply merge two objects, not clobbering objects on target with objects on source */
function deep_merge(target, source)
{
Log.warn("Doing a deep merge ...");
console.warn("Doing a deep merge ...");
for (var key in source) {
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
Log.warn(`Deeper merge on ${key}`);
console.warn(`Deeper merge on ${key}`);
deep_merge(target[key], source[key]);
}
else {
Log.warn(`Setting key ${key}`);
console.warn(`Setting key ${key}`);
target[key] = source[key];
}
}
@ -201,7 +201,7 @@ function samediff(from, to)
var same = [];
if (!to) return same;
if (typeof to !== 'object') {
Log.warn("'To' must be an object. Got " + to);
console.warn("'To' must be an object. Got " + to);
return same;
}
Object.keys(from).forEach(function(k) {

View file

@ -303,7 +303,7 @@ var editor = {
redo() {
if (Object.empty(this.backshots)) {
Log.info("Nothing to redo.");
console.info("Nothing to redo.");
return;
}
@ -317,7 +317,7 @@ var editor = {
undo() {
if (Object.empty(this.snapshots)) {
Log.info("Nothing to undo.");
console.info("Nothing to undo.");
return;
}
this.unselect();
@ -363,7 +363,7 @@ var editor = {
this._sel_comp = x;
if (this._sel_comp) {
Log.info("sel comp is now " + this._sel_comp);
console.info("sel comp is now " + this._sel_comp);
Player.players[0].control(this._sel_comp);
}
},
@ -377,7 +377,7 @@ var editor = {
x.gizmo();
});
Shape.line(bb2points(cwh2bb([0,0],[Game.native.x,Game.native.y])).wrapped(1), Color.yellow);
render.line(bb2points(cwh2bb([0,0],[Game.native.x,Game.native.y])).wrapped(1), Color.yellow);
/* Draw selection box */
if (this.sel_start) {
@ -392,15 +392,14 @@ var editor = {
wh[1] = Math.abs(endpos[1] - this.sel_start[1]);
var bb = cwh2bb(c,wh);
Debug.boundingbox(bb, Color.Editor.select.alpha(0.1));
Shape.line(bb2points(bb).wrapped(1), Color.white);
render.line(bb2points(bb).wrapped(1), Color.white);
}
},
gui() {
/* Clean out killed objects */
this.selectlist = this.selectlist.filter(function(x) { return x.alive; });
Debug.coordinate(world2screen([0,0]));
Shape.cross(Mouse.pos, 5);
Debug.coordinate(Window.world2screen([0,0]));
GUI.text("WORKING LAYER: " + this.working_layer, [0,520]);
GUI.text("MODE: " + this.edit_mode, [0,500]);
@ -408,7 +407,7 @@ var editor = {
if (this.comp_info && this.sel_comp)
GUI.text(Input.print_pawn_kbm(this.sel_comp,false), [100,700],1);
Shape.cross(editor.edit_level.screenpos(),3,Color.blue);
render.cross(editor.edit_level.screenpos(),3,Color.blue);
var thiso = editor.get_this();
var clvl = thiso;
@ -457,13 +456,13 @@ var editor = {
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);
Shape.cross(x.screenpos(), 10, Color.blue);
render.cross(x.screenpos(), 10, Color.blue);
});
Object.entries(thiso.objects).forEach(function(x) {
var p = x[1].namestr();
GUI.text(p, x[1].screenpos().add([0,16]),1,editor.color_depths[depth]);
Shape.circle(x[1].screenpos(),10,Color.blue.alpha(0.3));
render.circle(x[1].screenpos(),10,Color.blue.alpha(0.3));
});
var mg = physics.pos_query(Mouse.worldpos,10);
@ -495,8 +494,8 @@ var editor = {
});
Debug.draw_grid(1, editor.grid_size, Color.Editor.grid.alpha(0.3));
var startgrid = screen2world([-20,0]).map(function(x) { return Math.snap(x, editor.grid_size); });
var endgrid = screen2world([Window.width, Window.height]);
var startgrid = Window.screen2world([-20,0]).map(function(x) { return Math.snap(x, editor.grid_size); });
var endgrid = Window.screen2world([Window.width, Window.height]);
var w_step = Math.round(editor.ruler_mark_px/Window.width * (endgrid.x-startgrid.x)/editor.grid_size)*editor.grid_size;
if (w_step === 0) w_step = editor.grid_size;
@ -505,12 +504,12 @@ var editor = {
if (h_step === 0) h_step = editor.grid_size;
while(startgrid[0] <= endgrid[0]) {
GUI.text(startgrid[0], [world2screen([startgrid[0], 0])[0],0]);
GUI.text(startgrid[0], [Window.world2screen([startgrid[0], 0])[0],0]);
startgrid[0] += w_step;
}
while(startgrid[1] <= endgrid[1]) {
GUI.text(startgrid[1], [0, world2screen([0, startgrid[1]])[1]]);
GUI.text(startgrid[1], [0, Window.world2screen([0, startgrid[1]])[1]]);
startgrid[1] += h_step;
}
@ -562,7 +561,7 @@ var editor = {
} else {
var path = sub.replaceAll('.', '/') + ".json";
var saveobj = obj.json_obj();
IO.slurpwrite(path, JSON.stringify(saveobj,null,1));
io.slurpwrite(path, JSON.stringify(saveobj,null,1));
if (obj === editor.edit_level) {
if (obj === editor.desktop) {
@ -608,7 +607,7 @@ editor.inputs.drop = function(str) {
}
editor.inputs.f9 = function() {
Log.warn("CAPTURING");
console.warn("CAPTURING");
cmd(173, "capture.bmp", 0, 0, 500, 500);
}
@ -787,7 +786,7 @@ editor.inputs['C-M-p'] = function() {
if (!Game.playing()) {
editor.start_play_ed();
}
Log.warn(`Starting edited level ...`);
console.warn(`Starting edited level ...`);
};
editor.inputs['C-M-p'].doc = "Start game from currently edited level.";
@ -841,8 +840,8 @@ editor.inputs['C-s'] = function() {
if (savejs.objects) saveobj.__proto__.objects = savejs.objects;
var path = prototypes.ur_stem(saveobj.ur.toString()) + ".json";
IO.slurpwrite(path, JSON.stringify(saveobj.__proto__,null,1));
Log.warn(`Wrote to file ${path}`);
io.slurpwrite(path, JSON.stringify(saveobj.__proto__,null,1));
console.warn(`Wrote to file ${path}`);
Object.values(saveobj.objects).forEach(function(x) { x.check_dirty(); });
@ -1504,11 +1503,11 @@ var replpanel = Object.copy(inputpanel, {
ecode += `var ${key} = editor.edit_level.objects['${key}'];`;
ecode += this.value;
Log.say(this.value);
console.say(this.value);
this.value = "";
this.caret = 0;
var ret = function() {return eval(ecode);}.call(repl_obj);
Log.say(ret);
console.say(ret);
},
resetscroll() {
@ -1544,7 +1543,7 @@ replpanel.inputs.tab = function() {
if (eval(`typeof ${keyobj.tofirst('.')}`) === 'object' && eval(`typeof ${keyobj.replace('.', '?.')}`) === 'object')
obj = eval(keyobj);
else if (this.value.includes('.')){
Log.say(`${this.value} is not an object.`);
console.say(`${this.value} is not an object.`);
return;
}
@ -1575,17 +1574,17 @@ replpanel.inputs.tab = function() {
keys = keys.map(function(x) {
if (typeof obj[x] === 'function')
return Esc.color(Color.Apple.orange) + x + Esc.reset;
return esc.color(Color.Apple.orange) + x + esc.reset;
if (Object.isObject(obj[x]))
return Esc.color(Color.Apple.purple) + x + Esc.reset;
return esc.color(Color.Apple.purple) + x + esc.reset;
if (Array.isArray(obj[x]))
return Esc.color(Color.Apple.green) + x + Esc.reset;
return esc.color(Color.Apple.green) + x + esc.reset;
return x;
});
if (keys.length > 1)
Log.repl(keys.join(', '));
console.repl(keys.join(', '));
};
replpanel.inputs['C-p'] = function()
{
@ -1992,7 +1991,7 @@ limited_editor.inputs['C-q'] = function()
var limited_editing = {};
limited_editing.inputs = {};
if (IO.exists("editor.config"))
if (io.exists("editor.config"))
load_configs("editor.config");
/* This is the editor level & camera - NOT the currently edited level, but a level to hold editor things */

View file

@ -1,49 +1,37 @@
"use math";
var files = {};
function load(file) {
var modtime = cmd(0, file);
files[file] = modtime;
}
globalThis.global = globalThis;
var cmd_args = function(cc)
function eval_env(script, env)
{
console.warn(cc);
env ??= {};
// script = `function() { ${script} }.call();`;
// return eval(script);
return function(str) { return eval(str); }.call(env, script);
}
eval_env.dov = `Counterpart to /load_env/, but with a string.`;
function load_env(file,env)
{
env ??= global;
var script = io.slurp(file);
eval_env(script, env);
// cmd(16, file, env);
// var script = io.slurp(file);
// cmd(123, script, env, file);
}
load_env.doc = `Load a given file with 'env' as **this**. Does not add to the global namespace.`;
function load(file) { return load_env(file);}
load.doc = `Load a given script file into the global namespace.`;
load("scripts/base.js");
load("scripts/std.js");
//load("scripts/lunr.js");
//var lunrtxt = load("scripts/lunr.js");
//eval(lunrtxt);
/* The global namespace file */
var prosp = {};
function run(file)
{
var modtime = cmd(119, file);
if (modtime === 0) {
Log.stack();
return false;
}
files[file] = modtime;
return cmd(117, file);
}
run.doc = `Load a given script file.`;
load.doc = `Load a given script file.`;
function run_env(file, env)
{
var script = IO.slurp(file);
return function(){return eval(script);}.call(env);
}
run_env.doc = `Load a given script file, evaluating it in the context of the object 'env'.`;
load("scripts/diff.js");
Log.level = 1;
console.level = 1;
load("scripts/color.js");
@ -51,41 +39,6 @@ function bb2wh(bb) {
return [bb.r-bb.l, bb.t-bb.b];
};
var 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],
};
Device.doc = `Device resolutions given as [x,y,inches diagonal].`;
var prosperon = {};
prosperon.version = cmd(255);
@ -169,34 +122,60 @@ var timer = {
load("scripts/tween.js");
var Render = {
var render = {
normal() { cmd(67);},
wireframe() { cmd(68); },
pass() {
},
pass() { },
};
Render.doc = {
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].`;
load("scripts/physics.js");
load("scripts/input.js");
load("scripts/sound.js");
load("scripts/ai.js");
load("scripts/geometry.js");
function screen2world(screenpos) {
if (Game.camera)
return Game.camera.view2world(screenpos);
return screenpos;
}
function world2screen(worldpos) { return Game.camera.world2view(worldpos); }
var Register = {
kbm_input(mode, btn, state, ...args) {
if (state === 'released') {
@ -228,7 +207,7 @@ var Register = {
var rawfn = `gamepad_${btn}_${statestr}`;
player.input(rawfn, ...args);
Action.actions.forEach(x => {
input.action.actions.forEach(x => {
if (x.inputs.includes(btn))
player.input(`action_${x.name}_${statestr}`, ...args);
});
@ -284,20 +263,10 @@ register(7, Register.kbm_input, Register);
Register.add_cb(8, "gamepad_input");
Register.add_cb(10, "draw");
register(9, Log.stack, this);
register(9, console.stack, this);
Register.gamepad_playermap[0] = Player.players[0];
var Signal = {
obj_begin(fn, go) {
register_collide(0, fn, go.body);
},
obj_separate(fn, go) {
register_collide(3,fn, go.body);
},
};
var Event = {
events: {},
@ -329,7 +298,7 @@ var Window = {
get width() { return cmd(48); },
get height() { return cmd(49); },
get dimensions() { return [this.width, this.height]; },
set name(str) { cmd(134, str); },
title(str) { cmd(134, str); },
boundingbox() {
return {
t: Window.height,
@ -340,141 +309,18 @@ var Window = {
},
};
Window.screen2world = function(screenpos) {
if (Game.camera)
return Game.camera.view2world(screenpos);
return screenpos;
}
Window.world2screen = function(worldpos) { return Game.camera.world2view(worldpos); }
Window.icon = function(path) { cmd(90, path); };
Window.icon.doc = "Set the icon of the window using the PNG image at path.";
function reloadfiles() {
Object.keys(files).forEach(function (x) { load(x); });
}
load("scripts/debug.js");
/*
function Color(from) {
var color = Object.create(Array);
Object.defineProperty(color, 'r', setelem(0));
Object.defineProperty(color, 'g', setelem(1));
Object.defineProperty(color, 'b', setelem(2));
Object.defineProperty(color, 'a', setelem(3));
color.a = color.g = color.b = color.a = 1;
Object.assign(color, from);
return color;
};
*/
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); }
load('scripts/spline.js');
load("scripts/components.js");
var Game = {
@ -484,7 +330,7 @@ var Game = {
Sound.master = Sound.bus.master;
},
native: Device.pc,
native: render.device.pc,
object_count() {
return cmd(214);
@ -588,6 +434,6 @@ Game.view_camera = function(cam)
cmd(61, Game.camera.body);
}
Window.name = "Prosperon (V0.1)";
Window.title(`Prosperon v${prosperon.version}`);
Window.width = 1280;
Window.height = 720;

View file

@ -1,4 +1,4 @@
prosp.obj_unique_name = function(name, obj)
prosperon.obj_unique_name = function(name, obj)
{
name = name.replaceAll('.', '_');
if (!(name in obj)) return name;
@ -15,7 +15,7 @@ var actor = {};
actor.spawn = function(script, config){
if (typeof script !== 'string') return;
var padawan = Object.create(actor);
compile_env(script, padawan, "script");
eval_env(script, padawan);
if (typeof config === 'object')
Object.merge(padawan, config);
@ -133,7 +133,7 @@ var gameobject_impl = {
set mass(x) { set_body(7,this.body,x); },
get mass() {
if (!(this.phys === Physics.dynamic))
if (!(this.phys === physics.dynamic))
return undefined;
return q_body(5, this.body);
@ -159,7 +159,7 @@ var gameobject_impl = {
get_moi() { return q_body(6, this.body); },
set_moi(x) {
if(x <= 0) {
Log.error("Cannot set moment of inertia to 0 or less.");
console.error("Cannot set moment of inertia to 0 or less.");
return;
}
set_body(13, this.body, x);
@ -291,7 +291,7 @@ var gameobject = {
},
cry(file) {
this.crying = Sound.play(file, Sound.bus.sfx);
this.crying = audio.sound.play(file, audio.sound.bus.sfx);
var killfn = () => {this.crying = undefined; console.warn("killed"); }
this.crying.hook = killfn;
return killfn;
@ -318,7 +318,7 @@ var gameobject = {
set_body(2,this.body,x);
this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i])));
},
screenpos() { return world2screen(this.worldpos()); },
screenpos() { return Window.world2screen(this.worldpos()); },
worldangle() { return Math.rad2turn(q_body(2,this.body)); },
sworldangle(x) { set_body(0,this.body,Math.turn2rad(x)); },
@ -385,8 +385,8 @@ var gameobject = {
shove_at(vec, at) { set_body(14,this.body,vec,at); },
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body, pos); },
this2screen(pos) { return world2screen(this.this2world(pos)); },
screen2this(pos) { return this.world2this(screen2world(pos)); },
this2screen(pos) { return Window.world2screen(this.this2world(pos)); },
screen2this(pos) { return this.world2this(Window.screen2world(pos)); },
dir_world2this(dir) { return cmd(160, this.body, dir); },
dir_this2world(dir) { return cmd(161, this.body, dir); },
@ -440,10 +440,10 @@ var gameobject = {
obj.timers.push(Register.physupdate.register(obj.physupdate.bind(obj)));
if (typeof obj.collide === 'function')
Signal.obj_begin(obj.collide.bind(obj), obj);
register_collide(0, obj.collide.bind(obj), obj.body);
if (typeof obj.separate === 'function')
Signal.obj_separate(obj.separate.bind(obj), obj);
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));
@ -668,7 +668,7 @@ var gameobject = {
rename_obj(name, newname) {
if (!this.objects[name]) {
Log.warn(`No object with name ${name}. Could not rename to ${newname}.`);
console.warn(`No object with name ${name}. Could not rename to ${newname}.`);
return;
}
if (name === newname) {
@ -690,7 +690,7 @@ var gameobject = {
add_component(comp, data) {
data ??= undefined;
if (typeof comp.make !== 'function') return;
var name = prosp.obj_unique_name(comp.toString(), this);
var name = prosperon.obj_unique_name(comp.toString(), this);
this[name] = comp.make(this);
this[name].comp = comp.toString();
this.components[name] = this[name];
@ -797,20 +797,20 @@ prototypes.from_file = function(file)
var script = undefined;
var json = undefined;
if (jsfile) script = IO.slurp(jsfile);
if (jsfile) script = io.slurp(jsfile);
try {
if (jsonfile) json = JSON.parse(IO.slurp(jsonfile));
if (jsonfile) json = JSON.parse(io.slurp(jsonfile));
} catch(e) {
Log.warn(`Unable to create json from ${jsonfile}. ${e}`);
console.warn(`Unable to create json from ${jsonfile}. ${e}`);
}
if (!json && !script) {
Log.warn(`Could not make ur from ${file}`);
if (!json && !jsfile) {
console.warn(`Could not make ur from ${file}`);
return undefined;
}
if (script)
compile_env(script, newur, file);
load_env(jsfile, newur);
json ??= {};
Object.merge(newur,json);
@ -871,8 +871,9 @@ prototypes.file2ur = function(file)
prototypes.get_ur = function(name)
{
if (!name) return;
if (!name) {
console.error(`Can't get ur from an undefined.`);
console.error(`Can't get ur from ${name}.`);
return;
}
var urpath = name;
@ -899,16 +900,16 @@ prototypes.get_ur_file = function(path, ext)
{
var urpath = prototypes.ur2file(path);
var file = urpath + ext;
if (IO.exists(file)) return file;
if (io.exists(file)) return file;
file = urpath + "/" + path.split('.').at(-1) + ext;
if (IO.exists(file)) return file;
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"));
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; });
@ -940,7 +941,7 @@ prototypes.resani = function(ur, path)
var res = ur.replaceAll('.', '/');
var restry = res + "/" + path;
while (!IO.exists(restry)) {
while (!io.exists(restry)) {
res = res.updir() + "/";
if (res === "/")
return path;
@ -953,15 +954,15 @@ prototypes.resani = function(ur, path)
prototypes.ur_dir = function(ur)
{
var path = ur.replaceAll('.', '/');
Log.warn(path);
Log.warn(IO.exists(path));
Log.warn(`${path} does not exist; sending ${path.dir()}`);
console.warn(path);
console.warn(io.exists(path));
console.warn(`${path} does not exist; sending ${path.dir()}`);
}
prototypes.ur_json = function(ur)
{
var path = ur.replaceAll('.', '/');
if (IO.exists(path))
if (io.exists(path))
path = path + "/" + path.name() + ".json";
else
path = path + ".json";
@ -972,7 +973,7 @@ prototypes.ur_json = function(ur)
prototypes.ur_stem = function(ur)
{
var path = ur.replaceAll('.', '/');
if (IO.exists(path))
if (io.exists(path))
return path + "/" + path.name();
else
return path;
@ -983,7 +984,7 @@ prototypes.ur_file_exts = ['.jso', '.json'];
prototypes.ur_folder = function(ur)
{
var path = ur.replaceAll('.', '/');
return IO.exists(path);
return io.exists(path);
}
prototypes.ur_pullout_folder = function(ur)
@ -994,6 +995,6 @@ prototypes.ur_pullout_folder = function(ur)
/* prototypes.ur_file_exts.forEach(function(e) {
var p = stem + e;
if (IO.exists(p))
if (io.exists(p))
*/
}

View file

@ -1,6 +1,8 @@
var Sphere = {};
Sphere.volume = function(r) { return Math.pi*r*r*r*4/3; };
Sphere.random = function(r,theta,phi)
var shape = {};
shape.sphere = {};
shape.circle = {};
shape.sphere.volume = function(r) { return Math.pi*r*r*r*4/3; };
shape.sphere.random = function(r,theta,phi)
{
if (typeof r === 'number') r = [r,r];
theta ??= [0,1];
@ -18,9 +20,48 @@ Sphere.random = function(r,theta,phi)
];
}
var Circle = {};
Circle.area = function(r) { return Math.pi*r*r; };
Circle.random = function(r,theta)
shape.circle.area = function(r) { return Math.pi*r*r; };
shape.circle.random = function(r,theta)
{
return Sphere.random(r,theta).xz;
return shape.sphere.random(r,theta).xz;
}
shape.box = function(w,h) {
w /= 2;
h /= 2;
var points = [
[w,h],
[-w,h],
[-w,-h],
[w,-h]
];
return points;
};
shape.ngon = function(radius, n) {
return shape.arc(radius,360,n);
};
shape.arc = function(radius, angle, n, start) {
start ??= 0;
start = Math.deg2rad(start);
if (angle >= 360)
angle = 360;
if (n <= 1) return [];
var points = [];
angle = Math.deg2rad(angle);
var arclen = angle/n;
for (var i = 0; i < n; i++)
points.push(Vector.rotate([radius,0], start + (arclen*i)));
return points;
};
shape.circle.points = function(radius, n) {
if (n <= 1) return [];
return shape.arc(radius, 360, n);
};

View file

@ -199,7 +199,7 @@ Mum.button = Mum.text._int.extend({
hovered:{
color: Color.red
},
action() { Log.warn("Button has no action."); },
action() { console.warn("Button has no action."); },
});
Mum.window = Mum.extend({
@ -229,7 +229,7 @@ Mum.window = Mum.extend({
Mum.image = Mum.extend({
start() {
if (!this.path) {
Log.warn("Mum image needs a path.");
console.warn("Mum image needs a path.");
this.draw = function(){};
return;
}
@ -275,7 +275,7 @@ GUI.window = function(pos, wh, color)
var p = pos.slice();
p.x += wh.x/2;
p.y += wh.y/2;
Shape.box(p,wh,color);
render.box(p,wh,color);
}
GUI.flush = function() { cmd(141); };

View file

@ -1,4 +1,4 @@
var Input = {
var input = {
setgame() { cmd(77); },
setnuke() { cmd(78); },
};
@ -6,9 +6,40 @@ var Input = {
var Mouse = {
get pos() { return cmd(45); },
screenpos() { return cmd(45); },
get worldpos() { return screen2world(cmd(45)); },
get worldpos() { return Window.screen2world(cmd(45)); },
disabled() { cmd(46, 1); },
normal() { cmd(46, 0);},
mode(m) {
if (Mouse.custom[m])
cmd(97, Mouse.custom[m]);
else
cmd(17, m);
},
set_custom_cursor(img, mode) {
mode ??= Mouse.cursor.default;
if (!img)
delete Mouse.custom[mode];
else {
cmd(97, img);
Mouse.custom[mode] = img;
}
},
custom:[],
cursor: {
default: 0,
arrow: 1,
ibeam: 2,
cross: 3,
hand: 4,
ew: 5,
ns: 6,
nwse: 7,
nesw: 8,
resize: 9,
no: 10
},
};
Mouse.doc = {};
@ -24,7 +55,7 @@ var Keys = {
super() { return cmd(50, 343); },
};
Input.state2str = function(state) {
input.state2str = function(state) {
if (typeof state === 'string') return state;
switch (state) {
case 0:
@ -36,7 +67,7 @@ Input.state2str = function(state) {
}
}
Input.print_pawn_kbm = function(pawn) {
input.print_pawn_kbm = function(pawn) {
if (!('inputs' in pawn)) return;
var str = "";
for (var key in pawn.inputs) {
@ -46,7 +77,7 @@ Input.print_pawn_kbm = function(pawn) {
return str;
};
Input.print_md_kbm = function(pawn) {
input.print_md_kbm = function(pawn) {
if (!('inputs' in pawn)) return;
var str = "";
@ -60,13 +91,13 @@ Input.print_md_kbm = function(pawn) {
return str;
};
Input.has_bind = function(pawn, bind) {
input.has_bind = function(pawn, bind) {
return (typeof pawn.inputs?.[bind] === 'function');
};
var Action = {
input.action = {
add_new(name) {
var action = Object.create(Action);
var action = Object.create(input.action);
action.name = name;
action.inputs = [];
this.actions.push(action);
@ -167,7 +198,7 @@ var Player = {
print_pawns() {
for (var pawn of this.pawns.reversed())
Log.say(pawn.toString());
console.say(pawn.toString());
},
create() {

View file

@ -1,4 +1,4 @@
/* On collisions, entities are sent a 'hit' object, which looks like this: */
/* On collisions, entities are sent a 'hit' object, which looks like this:
var HIT = {
normal: "The normal of the collision point.",
hit: "The gameobject of the object that collided.",
@ -8,13 +8,13 @@ var HIT = {
depth: "Depth of the contact.",
};
var Physics = {
*/
var physics = {
dynamic: 0,
kinematic: 1,
static: 2,
};
var physics = {
pos_query(pos, give) {
give ??= 25;
return cmd(44, pos, give);
@ -43,7 +43,7 @@ physics.doc.pos_query = "Returns any object colliding with the given point.";
physics.doc.box_query = "Returns an array of body ids that collide with a given box.";
physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box.";
var Collision = {
physics.collision = {
types: {},
num: 32,
set_collide(a, b, x) {
@ -57,17 +57,17 @@ var Collision = {
},
};
for (var i = 0; i < Collision.num; i++) {
Collision.types[i] = [];
for (var j = 0; j < Collision.num; j++)
Collision.types[i][j] = false;
for (var i = 0; i < physics.collision.num; i++) {
physics.collision.types[i] = [];
for (var j = 0; j < physics.collision.num; j++)
physics.collision.types[i][j] = false;
};
Collision.sync();
physics.collision.sync();
var Warp = {};
Warp.gravity = function() { return cmd(253); }
Warp.damp = function() { return cmd(254); }
physics.warp = {};
physics.warp.gravity = function() { return cmd(253); }
physics.warp.damp = function() { return cmd(254); }
physics.gravity = Warp.gravity();
physics.damp = Warp.damp();
physics.gravity = physics.warp.gravity();
physics.damp = physics.warp.damp();

View file

@ -1,6 +1,6 @@
Game.play();
if (!IO.exists("game.js"))
if (!io.exists("game.js"))
load("scripts/nogame.js");
else
load("game.js");

View file

@ -1,20 +1,26 @@
var Sound = {
var audio = {};
audio.sound = {
bus: {},
samplerate() { return cmd(198); },
sounds: [], /* array of loaded sound files */
play(file, bus) {
if (!IO.exists(file)) {
Log.error(`Cannot play sound ${file}: does not exist.`);
if (!io.exists(file)) {
console.error(`Cannot play sound ${file}: does not exist.`);
return;
}
var src = DSP.source(file);
bus ??= Sound.bus.master;
var src = audio.dsp.source(file);
bus ??= sound.bus.master;
// src.plugin(bus);
return src;
},
doc: {
play: "Play the given file once.",
volume: "Set the volume. 0 is no sound and 100 is loudest."
},
};
var DSP = {
audio.dsp = {
mix(to) {
var n = cmd(181);
if (to) n.plugin(to);
@ -31,8 +37,8 @@ var DSP = {
},
allpass(secs, decay) {
var composite = {};
var fwd = DSP.fwd_delay(secs,-decay);
var fbk = DSP.delay(secs,decay);
var fwd = audio.dsp.fwd_delay(secs,-decay);
var fbk = audio.dsp.delay(secs,decay);
composite.id = fwd.id;
composite.plugin = composite.plugin.bind(fbk);
composite.unplug = dsp_node.unplug.bind(fbk);
@ -77,12 +83,7 @@ var DSP = {
},
};
Sound.play.doc = "Play the given file once.";
Sound.doc = {};
Sound.doc.volume = "Set the master volume. 0 is no sound and 100 is loudest.";
DSP.doc = {
audio.dsp.doc = {
delay: "Delays the input by secs, multiplied by decay",
fwd_delay: "Forward feedback delays the input by secs, multiplied by decay",
allpass: "Composite node of a delay and fwd_delay",
@ -105,10 +106,10 @@ Object.mixin(cmd(180).__proto__, {
set volume(x) { this.gain = x; },
});
/*Object.mixin(DSP.source().__proto__, {
/*Object.mixin(audio.dsp.source().__proto__, {
frames() { return cmd(197,this); },
length() { return this.frames()/Sound.samplerate(); },
time() { return this.frame/Sound.samplerate(); },
length() { return this.frames()/sound.samplerate(); },
time() { return this.frame/sound.samplerate(); },
pct() { return this.time()/this.length(); },
});
*/

View file

@ -1,22 +1,5 @@
function compile_env(str, env, file)
{
file ??= "unknown";
return cmd(123, str, env, file);
}
function fcompile_env(file, env) { return compile_env(IO.slurp(file), env, file); }
function buf2hex(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join(' ');
}
var OS = {};
OS.cwd = function() { return cmd(144); }
OS.exec = function(s) { cmd(143, s); }
OS.cwd.doc = "Get the absolute path of the current working directory.";
OS.exec.doc = "Run a command line instruction, and return when it finishes.";
os.cwd.doc = "Get the absolute path of the current working directory.";
os.env.doc = "Return the value of the environment variable v.";
var Resources = {};
Resources.images = ["png", "jpg", "jpeg", "gif"];
@ -46,7 +29,26 @@ Resources.texture.dimensions = function(path) { return cmd(64,path); }
Resources.gif = {};
Resources.gif.frames = function(path) { return cmd(139,path); }
var Log = {
Resources.replstrs = function(path)
{
var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
script = script.replace(regexp,function(str) {
if (str[1] === "/")
return str.rm(1);
if (str[1] === "@")
return str.rm(1).splice(1, "playerpath/");
return str.splice(1, stem + "/");
});
return script;
}
var console = {
set level(x) { cmd(92,x); },
get level() { return cmd(93); },
print(msg, lvl) {
@ -94,7 +96,8 @@ var Log = {
cmd(91,msg);
},
say(msg) { Log.write(msg + '\n'); },
log(msg) { console.say(time.text(time.now(), 'yyyy-m-dd hh:nn:ss') + " " + str); },
say(msg) { console.write(msg + '\n'); },
repl(msg) { cmd(142, msg + '\n'); },
stack(skip = 0) {
@ -103,18 +106,30 @@ var Log = {
var n = stack.next('\n',0)+1;
for (var i = 0; i < skip; i++)
n = stack.next('\n', n)+1;
Log.write(err.name);
Log.write(err.message);
Log.write(err.stack);
// Log.write(stack);
console.write(err.name);
console.write(err.message);
console.write(err.stack);
// console.write(stack);
},
clear() {
cmd(146);
},
assert(assertion, msg, obj) {
if (!assertion) {
console.error(msg);
console.stack();
}
},
};
Log.doc = {
var say = function(msg) {
console.say(msg);
}
say.doc = "Print to std out with an appended newline.";
console.doc = {
level: "Set level to output logging to console.",
info: "Output info level message.",
warn: "Output warn level message.",
@ -128,7 +143,7 @@ Log.doc = {
};
/*
IO path rules. Starts with, meaning:
io path rules. Starts with, meaning:
"@": playerpath
"/": game room
"#": Force look locally (instead of in db first)
@ -136,10 +151,10 @@ Log.doc = {
"": Local path relative to script defined in
*/
var IO = {
var io = {
exists(file) { return cmd(65, file);},
slurp(file) {
if (IO.exists(file))
if (io.exists(file))
return cmd(38,file);
else
throw new Error(`File ${file} does not exist; can't slurp`);
@ -154,7 +169,7 @@ var IO = {
return cmd(39, data, file);
},
extensions(ext) {
var paths = IO.ls();
var paths = io.ls();
paths = paths.filter(function(str) { return str.ext() === ext; });
return paths;
},
@ -179,7 +194,7 @@ var IO = {
cmd(258, dir);
},
glob(pat) {
var paths = IO.ls();
var paths = io.ls();
pat = pat.replaceAll(/([\[\]\(\)\^\$\.\|\+])/g, "\\$1");
pat = pat.replaceAll('**', '.*');
pat = pat.replaceAll(/[^\.]\*/g, '[^\\/]*');
@ -190,7 +205,7 @@ var IO = {
},
};
IO.doc = {
io.doc = {
doc: "Functions for filesystem input/output commands.",
exists: "Returns true if a file exists.",
slurp: "Returns the contents of given file as a string.",
@ -204,25 +219,6 @@ IO.doc = {
glob: "Glob files in game directory.",
};
var Parser = {};
Parser.replstrs = function(path)
{
var script = IO.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
script = script.replace(regexp,function(str) {
if (str[1] === "/")
return str.rm(1);
if (str[1] === "@")
return str.rm(1).splice(1, "playerpath/");
return str.splice(1, stem + "/");
});
Log.warn(script);
}
var Cmdline = {};
Cmdline.cmds = [];
@ -243,13 +239,11 @@ Cmdline.register_order = function(order, fn, doc, usage) {
}
Cmdline.register_order("edit", function() {
if (!IO.exists(".prosperon")) {
IO.mkdir(".prosperon");
var project = {};
project.version = prosperon.version;
project.revision = prosperon.revision;
IO.slurpwrite(".prosperon/project", json.encode(project));
if (!io.exists(".prosperon")) {
say("No game to edit. Try making one with 'prosperon init'.");
return;
}
Game.engine_start(function() {
load("scripts/editor.js");
load("editorconfig.js");
@ -257,17 +251,39 @@ Cmdline.register_order("edit", function() {
});
}, "Edit the project in this folder. Give it the name of an UR to edit that specific object.", "?UR?");
Cmdline.register_order("play", function() {
if (!IO.exists(".prosperon")) {
IO.mkdir(".prosperon");
var project = {};
project.version = prosperon.version;
project.revision = prosperon.revision;
IO.slurpwrite(".prosperon/project", json.encode(project));
Cmdline.register_order("init", function() {
if (io.exists(".prosperon")) {
say("Already a game here.");
return;
}
if (!(io.ls().length === 0)) {
say("Directory is not empty. Make an empty one and init there.");
return;
}
io.mkdir(".prosperon");
var project = {};
project.version = prosperon.version;
project.revision = prosperon.revision;
io.slurpwrite(".prosperon/project", json.encode(project));
}, "Turn the directory into a Prosperon game.");
Cmdline.register_order("play", function() {
if (!io.exists(".prosperon/project")) {
say("No game to play. Try making one with 'prosperon init'.");
return;
}
var project = json.decode(io.slurp(".prosperon/project"));
Game.engine_start(function() {
load("config.js");
load("game.js");
if (project.icon) Window.icon(project.icon);
if (project.title) Window.title(project.title);
say(project.title);
});
}, "Play the game present in this folder.");
@ -276,7 +292,7 @@ Cmdline.register_order("pack", function(str) {
if (str.length === 0)
packname = "test.cdb";
else if (str.length > 1) {
Log.warn("Give me a single filename for the pack.");
console.warn("Give me a single filename for the pack.");
return;
} else
packname = str[0];
@ -295,11 +311,13 @@ Cmdline.register_order("build", function() {
}, "Build static assets for this project.");
Cmdline.register_order("api", function(obj) {
if (!obj[0])
if (!obj[0]) {
Cmdline.print_order("api");
return;
}
load("scripts/editor.js");
var api = API.print_doc(obj[0]);
var api = Debug.api.print_doc(obj[0]);
if (!api)
return;
@ -308,15 +326,15 @@ Cmdline.register_order("api", function(obj) {
Cmdline.register_order("compile", function(argv) {
for (var file of argv) {
var comp = IO.compile(file);
IO.slurpwrite(file + ".byte", comp);
var comp = io.compile(file);
io.slurpwrite(file + "c", comp);
}
}, "Compile one or more provided files into bytecode.", "FILE ...");
Cmdline.register_order("input", function(pawn) {
load("scripts/editor.js");
say(`## Input for ${pawn}`);
eval(`say(Input.print_md_kbm(${pawn}));`);
eval(`say(input.print_md_kbm(${pawn}));`);
}, "Print input documentation for a given object as markdown. Give it a file to save the output to", "OBJECT ?FILE?");
Cmdline.register_order("run", function(script) {
@ -326,12 +344,12 @@ Cmdline.register_order("run", function(script) {
return;
}
if (IO.exists(script))
if (io.exists(script))
try {
if (script.endswith(".byte"))
if (script.endswith("c"))
cmd(261, script);
else
run(script);
load(script);
} catch(e) { }
else {
var ret = eval(script);
@ -339,6 +357,8 @@ Cmdline.register_order("run", function(script) {
}
}, "Run a given script. SCRIPT can be the script itself, or a file containing the script", "SCRIPT");
Cmdline.orders.script = Cmdline.orders.run;
Cmdline.print_order = function(fn)
{
if (typeof fn === 'string')
@ -388,66 +408,41 @@ function cmd_args(cmdargs)
}
Cmdline.orders[cmds[0]](cmds.slice(1));
}
Cmdline.register_order("clean", function(argv) {
say("Cleaning not implemented.");
return;
for (var i = 1; i < cmds.length; i++) {
if (cmds[i][0] !== '-') {
Log.warn(`Command '${cmds[i]}' should start with a '-'.`);
continue;
}
var c = Cmdline.cmds.find(function(cmd) { return cmd.flag === cmds[i].slice(1); });
if (!c) {
Log.warn(`Command ${cmds[i]} not recognized.`);
continue;
}
var sendstr = [];
var j = i+1;
while (cmds[j] && cmds[j][0] !== '-') {
sendstr.push(cmds[j]);
j++;
}
c.fn(sendstr);
i = j-1;
}
}
var STD = {};
STD.exit = function(status)
{
cmd(147,status);
}
Cmdline.register_cmd("l", function(n) {
Log.level = n;
}, "Set log level.");
Cmdline.register_cmd("cjson", function(json) {
var f = json[0];
if (!IO.exists(f)) {
Log.warn(`File ${f} does not exist.`);
STD.exit(1);
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.parse(IO.slurp(f));
var j = json.decode(io.slurp(f));
for (var k in j) {
for (var k in j)
if (k in j.objects)
delete j[k];
}
Log.warn(j);
console.warn(j);
for (var k in j.objects) {
var o = j.objects[k];
samediff(o, ur[o.ur]);
}
Log.say(j);
say(j);
}, "Clean up a given object file.", "JSON ...");
STD.exit(0);
}, "Clean up a jso file.");
Cmdline.register_cmd("l", function(n) {
console.level = n;
}, "Set log level.");

View file

@ -345,7 +345,7 @@ texteditor.open_fn = function(fnstr)
{
var fn = eval(fnstr);
if (!fn) {
Log.warn(`${fnstr} is not a function.`);
console.warn(`${fnstr} is not a function.`);
return;
}
this.src = fnstr;

View file

@ -421,3 +421,12 @@ void free_drawmodel(struct drawmodel *dm) {
free(dm);
}
void material_free(material *mat)
{
}
void mesh_free(mesh *m)
{
}

View file

@ -189,8 +189,7 @@ void input_dropped_files(int n)
argv[0] = jstr("emacs");
argv[1] = jstr("drop");
argv[2] = jstr("pressed");
char *path = rebase_path(sapp_get_dropped_file_path(0));
argv[3] = str2js(path+1);
argv[3] = str2js(sapp_get_dropped_file_path(0));
script_callee(pawn_callee, 4, argv);
JS_FreeValue(js,argv[3]);
}
@ -323,6 +322,15 @@ int key_is_num(int key) {
void cursor_hide() { sapp_show_mouse(0); }
void cursor_show() { sapp_show_mouse(1); }
void cursor_img(const char *path)
{
/* NSString *str = [NSString stringWithUTF8String:path];
NSImage *img = [[NSImage alloc] initWithContentsOfFile:str];
NSCursor *custom = [[NSCursor alloc] initWithImage:img hotSpot:NSMakePoint(0,0)];
[custom set];
*/
}
int action_down(int key) { return key_states[key] == INPUT_DOWN; }
int action_up(int key) { return key_states[key] == INPUT_UP; }

View file

@ -18,6 +18,7 @@ void input_poll(double wait);
void cursor_hide();
void cursor_show();
void cursor_img(const char *path);
void set_mouse_mode(int mousemode);
void input_mouse(int btn, int state, uint32_t mod);

View file

@ -25,9 +25,17 @@
#include <assert.h>
#include "resources.h"
#include <sokol/sokol_time.h>
#include <sokol/sokol_app.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#if (defined(_WIN32) || defined(__WIN32__))
#include <direct.h>
#define mkdir(x,y) _mkdir(x)
#endif
#include "nota.h"
@ -90,6 +98,11 @@ static JSValue constraint2js(constraint *c)
static JSValue sound_proto;
sound *js2sound(JSValue v) { return js2dsp_node(v)->data; }
#define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \
JS_SetPropertyStr(js, globalThis, #NAME, NAME); \
#define QJSCLASSPREP(TYPE) \
JS_NewClassID(&js_##TYPE##_id);\
JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
@ -731,6 +744,15 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
gameobject_draw_debug(js2gameobject(argv[1]));
break;
case 16:
str = js2str(argv[1]);
file_eval_env(str,argv[2]);
break;
case 17:
sapp_set_mouse_cursor(js2int(argv[1]));
break;
case 18:
shape_set_sensor(js2ptr(argv[1]), JS_ToBool(js, argv[2]));
break;
@ -1026,6 +1048,11 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 96:
id2sprite(js2int(argv[1]))->color = js2color(argv[2]);
break;
case 97:
str = js2str(argv[1]);
cursor_img(str);
break;
case 103:
ret = vec2js(js2gameobject(argv[1])->scale.XY);
break;
@ -1178,11 +1205,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 143:
str = JS_ToCString(js, argv[1]);
system(str);
break;
case 144:
ret = str2js(DATA_PATH);
if (!getenv(str)) ret = JS_UNDEFINED;
else
ret = str2js(getenv(str));
break;
case 145:
@ -1899,8 +1924,25 @@ JSValue js_emitter_emit(JSContext *js, JSValueConst this, int argc, JSValue *arg
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_global_funcs[] = {
JSValue js_os_cwd(JSContext *js, JSValueConst this)
{
char cwd[PATH_MAX];
getcwd(cwd, sizeof(cwd));
return str2js(cwd);
}
JSValue js_os_env(JSContext *js, JSValueConst this, int argc, JSValue *argv)
{
char *str = js2str(argv[0]);
JSValue ret = JS_UNDEFINED;
if (getenv(str)) ret = str2js(getenv(str));
JS_FreeCString(js,str);
return ret;
}
static const JSCFunctionListEntry js_os_funcs[] = {
MIST_CFUNC_DEF("cwd", 0, js_os_cwd),
MIST_CFUNC_DEF("env", 1, js_os_env),
};
static const JSCFunctionListEntry js_emitter_funcs[] = {
@ -2045,6 +2087,11 @@ JSValue duk_cmd_points(JSContext *js, JSValueConst this, int argc, JSValueConst
const char *STRTEST = "TEST STRING";
JSValue duk_profile_js2num(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
{
}
JSValue duk_profile(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
{
int cmd = js2int(argv[0]);
@ -2153,6 +2200,8 @@ void ffi_load() {
QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(constraint);
QJSGLOBALCLASS(os);
}
void ffi_stop()

View file

@ -25,9 +25,6 @@
#include "core.cdb.h"
char *DATA_PATH = NULL; /* The top level asset path, where the executable resides */
char *PREF_PATH = NULL; /* Path to where the program can write data to, usually for save files etc. */
char **prefabs;
static const char *cur_ext = NULL;
@ -35,18 +32,10 @@ struct dirent *c_dirent = NULL;
char pathbuf[MAXPATH + 1];
const char *DB_NAME = "test.db";
static struct cdb corecdb;
static struct cdb game_cdb;
void resources_init() {
DATA_PATH = malloc(MAXPATH);
getcwd(DATA_PATH, MAXPATH);
if (!PREF_PATH)
PREF_PATH = strdup("./tmp/");
int fd = open("test.cdb", O_RDONLY);
cdb_init(&game_cdb, fd);
cdb_initf(&corecdb, core_cdb, core_cdb_len);
@ -80,23 +69,6 @@ char *dirname(const char *path)
return dir;
}
char *rebase_path(const char *path)
{
int off = 0;
while (path[off] == DATA_PATH[off]) {
off++;
if (!path[off] || !DATA_PATH[off]) break;
}
return path+off;
}
FILE *res_open(char *path, const char *tag) {
strncpy(pathbuf, DATA_PATH, MAXPATH);
strncat(pathbuf, path, MAXPATH);
FILE *f = fopen(pathbuf, tag);
return f;
}
char *seprint(char *fmt, ...)
{
va_list args;
@ -115,27 +87,6 @@ char *seprint(char *fmt, ...)
static char *ext_paths = NULL;
#ifndef __EMSCRIPTEN__
static int ext_check(const char *path, const struct stat *sb, int typeflag) {
if (typeflag == FTW_F) {
const char *ext = strrchr(path, '.');
if (ext != NULL && !strcmp(ext, cur_ext)) {
char newstr[255];
strncpy(newstr, path, 255);
arrput(prefabs, newstr);
}
}
return 0;
}
void fill_extensions(char *paths, const char *path, const char *ext) {
cur_ext = ext;
arrfree(paths);
ext_paths = paths;
ftw(".", ext_check, 10);
}
static char **ls_paths = NULL;
static int ls_ftw(const char *path, const struct stat *sb, int typeflag)

View file

@ -8,7 +8,6 @@
extern char *DATA_PATH;
void resources_init();
void fill_extensions(char *paths, const char *path, const char *ext);
char *get_filename_from_path(char *path, int extension);
char *get_directory_from_path(char *path);
char *str_replace_ext(const char *s, const char *newext);
@ -16,7 +15,6 @@ FILE *res_open(char *path, const char *tag);
FILE *path_open(const char *tag, const char *fmt, ...);
char **ls(const char *path);
int cp(const char *p1, const char *p2);
char *rebase_path(const char *path); /* given a global path, rebase to the local structure */
int fexists(const char *path);
FILE *fopen_mkdir(const char *path, const char *mode);

View file

@ -277,7 +277,8 @@ void script_call_fn_arg(JSValue fn, JSValue arg)
void out_memusage(const char *file)
{
FILE *f = fopen_mkdir(file, "w");
FILE *f = fopen(file, "w");
if (!f) return;
JSMemoryUsage jsmem;
JS_ComputeMemoryUsage(rt, &jsmem);
JS_DumpMemoryUsage(f, &jsmem, rt);

View file

@ -3770,6 +3770,7 @@ _SOKOL_PRIVATE void _sapp_macos_update_cursor(sapp_mouse_cursor cursor, bool sho
[_sapp.macos.cursors[cursor] set];
}
else {
printf("CURSOR\n");
[[NSCursor arrowCursor] set];
}
}

View file

@ -260,7 +260,7 @@ static sapp_desc start_desc = {
.logger.func = sg_logging,
};
void app_name(const char *name) { start_desc.window_title = strdup(name); }
void app_name(const char *name) { sapp_set_window_title(name); }
int main(int argc, char **argv) {
#ifndef NDEBUG