1
1
Fork 0
mirror of https://github.com/boxgaming/qbjs.git synced 2024-05-12 08:00:12 +00:00

Compare commits

...

5 commits

9 changed files with 801 additions and 359 deletions

173
gx/gx.js
View file

@ -5,6 +5,7 @@ var GX = new function() {
var _bg = [];
var _images = [];
var _entities = [];
var _entities_active = [];
var _entity_animations = [];
var _scene = {};
var _tileset = {};
@ -37,8 +38,6 @@ var GX = new function() {
var _vfs = new VFS();
var _vfsCwd = null;
var _glcanvas = null;
// javascript specific
var _onGameEvent = null;
var _pressedKeys = {};
@ -59,7 +58,6 @@ var GX = new function() {
function _reset() {
// stop any sounds that are currently playing
_soundStopAll();
_framerate = 60;
_bg = [];
_images = [];
@ -216,11 +214,6 @@ var GX = new function() {
_canvas.height = height;
_ctx = _canvas.getContext("2d");
//_glcanvas.width = width;
//_glcanvas.height = height;
//_glctx = _glcanvas.getContext("webgl");
////__GL.init(_glctx);
var footer = document.getElementById("gx-footer");
footer.style.width = width;
@ -297,8 +290,7 @@ var GX = new function() {
// handled externally.
function _sceneDraw() {
if (_map_loading) { return; }
var ei, ei2, frame;
frame = _scene.frame % GX.frameRate() + 1;
var frame = _scene.frame % GX.frameRate() + 1;
// If the screen has been resized, resize the destination screen image
//If _Resize And Not GXSceneEmbedded Then
@ -318,6 +310,17 @@ var GX = new function() {
// Call out to any custom screen drawing
_customDrawEvent(GX.EVENT_DRAWBG);
// Initialize the renderable entities
_entities_active = [];
for (var ei=1; ei <= _entities.length; ei++) {
var e = _entities[ei-1];
if (!e.screen) {
if (_rectCollide(e.x, e.y, e.width, e.height, GX.sceneX(), GX.sceneY(), GX.sceneWidth(), GX.sceneHeight())) {
_entities_active.push(ei);
}
}
}
// Draw the map tiles
GX.mapDraw();
@ -325,19 +328,7 @@ var GX = new function() {
_customDrawEvent(GX.EVENT_DRAWMAP);
// Draw the entities
for (var ei = 1; ei <= _entities.length; ei++) {
var e = _entities[ei-1];
if (!e.screen) {
if (_rectCollide(e.x, e.y, e.width, e.height, GX.sceneX(), GX.sceneY(), GX.sceneWidth(), GX.sceneHeight())) {
_entityDraw(e);
}
}
if (e.animate > 0) {
if (frame % (GX.frameRate() / e.animate) == 0) {
GX.entityFrameNext(ei);
}
}
}
_drawEntityLayer(0);
// Draw the screen entities which should appear on top of the other game entities
// and have a fixed position
@ -345,6 +336,9 @@ var GX = new function() {
var e = _entities[ei-1];
if (e.screen) {
_entityDraw(e);
if (frame % (GX.frameRate() / e.animate) == 0) {
GX.entityFrameNext(ei);
}
}
}
@ -367,12 +361,6 @@ var GX = new function() {
// Call custom game update logic
_customEvent(GX.EVENT_UPDATE);
// Perform any movement for registered player entities
//Dim i As Integer
//For i = 1 To __gx_player_count
// __GX_PlayerAction i
//Next i
// Check for entity movement and collisions
// TODO: filter out non-moving entities
_sceneMoveEntities();
@ -767,19 +755,14 @@ var GX = new function() {
newent.coRight = 0;
newent.coBottom = 0;
newent.applyGravity = false;
newent.sequences = 0;
newent.mapLayer = 0;
_entities.push(newent);
var animation = [];
_entity_animations.push(animation);
/*
newent.sequences = Math.floor(_images[newent.image-1].height / height);
console.log(newent.sequences);
for (var i=0; i < newent.sequences; i++) {
animation.push({ frames: seqFrames });
}
*/
return _entities.length;
}
@ -818,7 +801,7 @@ var GX = new function() {
return _entities[eid-1].seqFrames;
}
else {
return _entity_animations[eid-1].frames;
return a[seq-1].frames;
}
}
@ -912,12 +895,12 @@ var GX = new function() {
return _entities[eid-1].sequences;
}
function _entityFrames (eid, seq) {
return _entityGetFrames(eid, seq); //_entity_animations[eid-1][seq-1].frames;
}
function _entityFrames (eid, seq, frames) {
_entity_animations[eid-1][seq-1] = { frames: frames };
console.log(eid + ":" + seq + ":" + frames);
if (frames != undefined) {
_entity_animations[eid-1][seq-1] = { frames: frames };
}
return _entityGetFrames(eid, seq); //_entity_animations[eid-1][seq-1].frames;
}
function _entityType (eid, etype) {
@ -927,6 +910,30 @@ var GX = new function() {
return _entities[eid-1].type
}
function _entityMapLayer (eid, layer) {
if (layer != undefined) {
_entities[eid-1].mapLayer = layer;
}
return _entities[eid-1].mapLayer;
}
function _drawEntityLayer (layer) {
var frame = _scene.frame % GX.frameRate() + 1;
for (var i=0; i < _entities_active.length; i++) {
var ei = _entities_active[i];
var e = _entities[ei-1];
if (e.mapLayer == layer) {
_entityDraw(e);
if (e.animate > 0) {
if (frame % (GX.frameRate() / e.animate) == 0) {
GX.entityFrameNext(ei);
}
}
}
}
}
function _entityApplyGravity (eid, gravity) {
if (gravity != undefined) {
_entities[eid-1].applyGravity = gravity;
@ -1052,41 +1059,33 @@ var GX = new function() {
});
_map_layers.push(_mapLayerInit());
}
function _mapLayerInsert (beforeLayer) {
if (beforeLayer < 1 || beforeLayer > GX.mapLayers()) { return; }
GX.mapLayerAdd();
for (var layer = GX.mapLayers(); layer > beforeLayer; layer--) {
for (var tile = 0; tile <= GX.mapRows() * GX.mapColumns(); tile++) {
_map_layers[layer-1][tile] = _map_layers[layer - 2][tile];
}
}
_map_layers[beforeLayer-1] = _mapLayerInit();
}
function _mapLayerRemove (removeLayer) {
if (removeLayer < 1 || removeLayer > GX.mapLayers() || GX.mapLayers < 2) { return; }
for (var layer = removeLayer; layer < GX.mapLayers(); layer++) {
for (var tile = 0; tile <= GX.mapRows() * GX.mapColumns(); tile++) {
_map_layers[layer-1][tile] = _map_layers[layer][tile];
}
}
_map_layer_info.pop();
_map_layers.pop();
_map.layers = GX.mapLayers() - 1;
}
/*
Sub GXMapLayerInsert (beforeLayer As Integer)
If beforeLayer < 1 Or beforeLayer > GXMapLayers Then Exit Sub
GXMapLayerAdd
Dim layer As Integer
Dim tile As Integer
For layer = GXMapLayers To beforeLayer + 1 Step -1
'gx_map_layer_info(layer) = gx_map_layer_info(layer - 1)
For tile = 0 To GXMapRows * GXMapColumns
__gx_map_layers(tile, layer) = __gx_map_layers(tile, layer - 1)
Next tile
Next layer
Dim blankTile As GXMapTile
For tile = 0 To GXMapRows * GXMapColumns
__gx_map_layers(tile, beforeLayer) = blankTile
Next tile
End Sub
Sub GXMapLayerRemove (removeLayer As Integer)
If removeLayer < 1 Or removeLayer > GXMapLayers Or GXMapLayers < 2 Then Exit Sub
Dim layer As Integer
Dim tile As Integer
For layer = removeLayer To GXMapLayers - 1
For tile = 0 To GXMapRows * GXMapColumns
__gx_map_layers(tile, layer) = __gx_map_layers(tile, layer + 1)
Next tile
Next layer
ReDim _Preserve __gx_map_layer_info(GXMapLayers - 1) As GXMapLayer
ReDim _Preserve __gx_map_layers(GXMapRows * GXMapColumns, GXMapLayers - 1) As GXMapTile
__gx_map.layers = GXMapLayers - 1
End Sub
Sub GXMapResize (columns As Integer, rows As Integer)
Dim tempMap(GXMapRows * GXMapColumns, GXMapLayers) As GXMapTile
Dim m1 As _MEM: m1 = _Mem(__gx_map_layers())
@ -1186,6 +1185,7 @@ var GX = new function() {
srow = srow + 1;
}
} // layer is not hidden
_drawEntityLayer(li);
}
// Perform tile animation
@ -1986,17 +1986,20 @@ var GX = new function() {
function _mouseY() {
return Math.round((_mousePos.y - _scene.offsetY) / _scene.scaleY);
};
}
function _mouseButton(button) {
// TODO: need to decide whether to keep this here
// it is not needed for GX - only to support QB64
return _mouseButtons[button-1];
};
}
function _mouseWheel() {
return _mouseWheelFlag;
var mw = _mouseWheelFlag;
_mouseWheelFlag = false;
return mw;
}
function _touchInput() {
var ti = _touchInputFlag;
_touchInputFlag = false;
@ -2488,8 +2491,6 @@ var GX = new function() {
this.ctx = function() { return _ctx; };
this.canvas = function() { return _canvas; };
this.gl = function() { return _glctx; };
this.glcanvas = function() { return _glcanvas; };
this.vfs = function() { return _vfs; };
this.vfsCwd = function(cwd) {
if (cwd != undefined) {
@ -2550,8 +2551,10 @@ var GX = new function() {
this.entityVX = _entityVX;
this.entityVY = _entityVY;
this.entityFrameNext = _entityFrameNext;
this.entityFrames = _entityFrames;
this.entityFrameSet = _entityFrameSet;
this.entityType = _entityType;
this.entityMapLayer = _entityMapLayer;
this.entityCollisionOffset = _entityCollisionOffset;
this.entityCollisionOffsetLeft = _entityCollisionOffsetLeft;
this.entityCollisionOffsetTop = _entityCollisionOffsetTop;
@ -2566,13 +2569,15 @@ var GX = new function() {
this.entitySequences = _entitySequences;
this.entityFrames = _entityFrames;
this.mapColumns = _mapColumns;
this.mapCreate = _mapCreate;
this.mapLoad = _mapLoad;
this.mapDraw = _mapDraw;
this.mapIsometric = _mapIsometric;
this.mapLayerAdd = _mapLayerAdd;
this.mapLayerInsert = _mapLayerInsert;
this.mapLayerRemove = _mapLayerRemove;
this.mapLayerInit = _mapLayerInit;
this.mapLayerVisible = _mapLayerVisible;
this.mapLayers = _mapLayers;

228
qb.js
View file

@ -235,53 +235,74 @@ var QB = new function() {
}
};
// Runtime Assertions
function _assertParam(param, arg) {
if (arg == undefined) { arg = 1; }
if (param == undefined) { throw new Error("Method argument " + arg + " is required"); }
}
function _assertNumber(param, arg) {
if (arg == undefined) { arg = 1; }
if (isNaN(param)) { throw new Error("Number required for method argument " + arg); }
}
// Extended QB64 Keywords
// --------------------------------------------
this.func__Acos = function(x) {
_assertNumber(x);
return Math.acos(x);
};
this.func__Acosh = function(x) {
_assertNumber(x);
return Math.acosh(x);
};
this.func__Arccot = function(x) {
_assertNumber(x);
return 2 * Math.atan(1) - Math.atan(x);
};
this.func__Arccsc = function(x) {
_assertNumber(x);
return Math.asin(1 / x);
};
this.func__Arcsec = function(x) {
_assertNumber(x);
return Math.acos(1 / x);
};
this.func__Alpha = function(rgb, imageHandle) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).a;
};
this.func__Alpha32 = function(rgb) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).a;
};
this.func__Asin = function(x) {
_assertNumber(x);
return Math.asin(x);
};
this.func__Asinh = function(x) {
_assertNumber(x);
return Math.asinh(x);
};
this.func__Atanh = function(x) {
_assertNumber(x);
return Math.atanh(x);
};
this.func__Atan2 = function(y, x) {
_assertNumber(x);
return Math.atan2(y, x);
};
@ -295,11 +316,13 @@ var QB = new function() {
};
this.func__Blue = function(rgb, imageHandle) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).b;
};
this.func__Blue32 = function(rgb) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).b;
};
@ -309,6 +332,7 @@ var QB = new function() {
};
this.func__Ceil = function(x) {
_assertNumber(x);
return Math.ceil(x);
};
@ -317,6 +341,7 @@ var QB = new function() {
};
this.func__CopyImage = function(srcImageId) {
_assertNumber(srcImageId);
var srcCanvas = _images[srcImageId].canvas;
var destImageId = QB.func__NewImage(srcCanvas.width, srcCanvas.height);
var ctx = _images[destImageId].ctx;
@ -326,22 +351,27 @@ var QB = new function() {
};
this.func__Cosh = function(x) {
_assertNumber(x);
return Math.cosh(x);
};
this.func__Cot = function(x) {
_assertNumber(x);
return 1 / Math.tan(x);
}
this.func__Coth = function(x) {
_assertNumber(x);
return 1 / Math.tanh(x);
};
this.func__Csc = function(x) {
_assertNumber(x);
return 1 / Math.sin(x);
};
this.func__Csch = function(x) {
_assertNumber(x);
return 1 / Math.sinh(x);
};
@ -350,10 +380,12 @@ var QB = new function() {
};
this.func__D2R = function(x) {
_assertNumber(x);
return x * Math.PI / 180;
};
this.func__D2G = function(x) {
_assertNumber(x);
return (x * 10 / 9);
};
@ -364,11 +396,13 @@ var QB = new function() {
};
this.func__Deflate = function(src) {
_assertParam(src);
var result = pako.deflate(src);//, { to: "string" });
return String.fromCharCode.apply(String, result);
};
this.sub__Delay = async function(seconds) {
_assertNumber(seconds);
await GX.sleep(seconds*1000);
};
@ -385,6 +419,7 @@ var QB = new function() {
};
this.sub__Dest = function(imageId) {
_assertNumber(imageId);
_flushScreenCache(_images[_activeImage]);
_activeImage = imageId;
};
@ -394,6 +429,7 @@ var QB = new function() {
};
this.func__DirExists = function(path) {
_assertParam(path);
var vfs = GX.vfs();
var dir = vfs.getNode(path, GX.vfsCwd());
if (dir && dir.type == vfs.DIRECTORY) {
@ -411,6 +447,7 @@ var QB = new function() {
};
this.sub__Echo = function(msg) {
_assertParam(msg);
console.log(msg);
};
@ -420,6 +457,7 @@ var QB = new function() {
};
this.func__FileExists = function(path) {
_assertParam(path);
var vfs = GX.vfs();
var file = vfs.getNode(path, GX.vfsCwd());
if (file && file.type == vfs.FILE) {
@ -429,6 +467,7 @@ var QB = new function() {
};
this.sub__Font = function(fnt) {
_assertNumber(fnt);
_font = fnt;
_locX = 0;
_lastTextX = 0;
@ -460,6 +499,9 @@ var QB = new function() {
};
this.sub__FreeImage = function(imageId) {
if (imageId == undefined) {
imageId = _activeImage;
}
_images[imageId] = undefined;
};
@ -483,15 +525,18 @@ var QB = new function() {
};
this.func__G2R = function(x) {
_assertNumber(x);
return (x * 9/10) * Math.PI/180;
};
this.func__Green = function(rgb, imageHandle) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).g;
};
this.func__Green32 = function(rgb) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).g;
};
@ -510,28 +555,33 @@ var QB = new function() {
}
this.func__Hypot = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 1);
return Math.hypot(x, y);
};
this.func__Inflate = function(src) {
_assertParam(src);
var result = pako.inflate(GX.vfs().textToData(src), { to: "string" });
return result;
};
this.func__InStrRev = function(arg1, arg2, arg3) {
var startIndex = +Infinity;
var strSource = "";
var strSearch = "";
if (arg3 != undefined) {
startIndex = arg1-1;
strSource = String(arg2);
strSearch = String(arg3);
}
else {
strSource = String(arg1);
strSearch = String(arg2);
}
return strSource.lastIndexOf(strSearch, startIndex)+1;
_assertParam(arg1, 1);
_assertParam(arg2, 2);
var startIndex = +Infinity;
var strSource = "";
var strSearch = "";
if (arg3 != undefined) {
startIndex = arg1-1;
strSource = String(arg2);
strSearch = String(arg3);
}
else {
strSource = String(arg1);
strSearch = String(arg2);
}
return strSource.lastIndexOf(strSearch, startIndex)+1;
};
this.sub__KeyClear = function(buffer) {
@ -555,6 +605,7 @@ var QB = new function() {
};
this.sub__Limit = async function(fps) {
_assertNumber(fps);
_flushAllScreenCache();
var frameMillis = 1000 / fps / 1.15;
await GX.sleep(0);
@ -574,6 +625,8 @@ var QB = new function() {
};
this.func__LoadFont = async function(name, size, opts) {
_assertParam(name, 1);
_assertParam(size, 2);
if (!isNaN(size)) {
size = size + "px";
}
@ -635,6 +688,7 @@ var QB = new function() {
this.func__LoadImage = async function(url) {
_assertParam(url);
var vfs = GX.vfs();
var vfsCwd = GX.vfsCwd();
var img = null;
@ -710,6 +764,7 @@ var QB = new function() {
};
this.func__MouseButton = function(button) {
_assertNumber(button);
return GX.mouseButton(button);
};
@ -718,6 +773,8 @@ var QB = new function() {
};
this.func__NewImage = function(iwidth, iheight, mode) {
_assertNumber(iwidth, 1);
_assertNumber(iheight, 2);
var canvas = document.createElement("canvas");
canvas.id = "qb-canvas-" + _nextImageId;
if (mode == 0) {
@ -812,7 +869,8 @@ var QB = new function() {
};
this.func__PrintWidth = function(s) {
if (!s) { return 0; }
_assertParam(s);
//if (!s) { return 0; }
var ctx = GX.ctx();
var f = _fonts[_font];
ctx.font = f.size + " " + f.name;
@ -824,6 +882,9 @@ var QB = new function() {
if (m == undefined) {
m = 1;
}
else {
_assertNumber(m);
}
return Math.PI * m;
}
@ -928,14 +989,18 @@ var QB = new function() {
}
this.func__R2D = function(x) {
_assertNumber(x);
return x*180/Math.PI;
};
this.func__R2G = function(x) {
_assertNumber(x);
return (x*(9/10))*180/Math.PI;
};
this.func__Readbit= function(x, y) {
this.func__Readbit = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 2);
var mask = 1 << y;
if ((x & mask) != 0) {
return -1;
@ -945,16 +1010,20 @@ var QB = new function() {
};
this.func__Red = function(rgb, imageHandle) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).r;
};
this.func__Red32 = function(rgb) {
_assertParam(rgb);
// TODO: implement corresponding logic when an image handle is supplied (maybe)
return _color(rgb).r;
};
this.func__Resetbit = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 2);
var mask = 1 << y;
return x & ~mask;
};
@ -986,6 +1055,9 @@ var QB = new function() {
};
this.func__RGBA = function(r, g, b, a) {
_assertNumber(r, 1);
_assertNumber(g, 2);
_assertNumber(b, 3);
if (a == undefined) {
a = 255;
}
@ -1004,6 +1076,7 @@ var QB = new function() {
}
this.func__Round = function(value) {
_assertNumber(value);
if (value < 0) {
return -Math.round(-value);
} else {
@ -1032,62 +1105,81 @@ var QB = new function() {
};
this.func__Sec = function(x) {
_assertNumber(x);
return 1 / Math.cos(x);
};
this.func__Sech = function(x) {
_assertNumber(x);
return 1 / Math.cosh(x);
};
this.func__Setbit = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 2);
var mask = 1 << y;
return x | mask;
};
this.func__Shl = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 2);
return x << y;
};
this.func__Shr = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 2);
return x >>> y;
};
this.func__Sinh = function(x) {
_assertNumber(x);
return Math.sinh(x);
};
this.func__Source = function() {
return _sourceImage;
}
};
this.sub__Source = function(imageId) {
_assertNumber(imageId);
_sourceImage = imageId;
}
};
this.sub__SndClose = function(sid) {
_assertNumber(sid);
GX.soundClose(sid);
};
this.func__SndOpen = function(filename) {
_assertParam(filename);
return GX.soundLoad(filename);
};
this.sub__SndPlay = function(sid) {
_assertNumber(sid);
GX.soundPlay(sid);
};
this.sub__SndLoop = function(sid) {
_assertNumber(sid);
GX.soundRepeat(sid);
};
this.sub__SndPause = function(sid) {
_assertNumber(sid);
GX.soundPause(sid);
};
this.sub__SndStop = function(sid) {
_assertNumber(sid);
GX.soundStop(sid);
};
this.sub__SndVol = function(sid, v) {
_assertNumber(sid, 1);
_assertNumber(v, 2);
GX.soundVolumne(sid, v);
};
@ -1096,16 +1188,21 @@ var QB = new function() {
}
this.func__Strcmp = function(x, y) {
_assertParam(x, 1);
_assertParam(y, 2);
return (( x == y ) ? 0 : (( x > y ) ? 1 : -1 ));
};
this.func__Stricmp = function(x, y) {
_assertParam(x, 1);
_assertParam(y, 2);
var a = x.toLowerCase();
var b = y.toLowerCase();
return (( a == b ) ? 0 : (( a > b ) ? 1 : -1 ));
};
this.func__Tanh = function(x) {
_assertParam(x);
return Math.tanh(x);
};
@ -1114,14 +1211,18 @@ var QB = new function() {
};
this.sub__Title = function(title) {
_assertParam(title);
document.title = title;
};
this.func__Trim = function(value) {
_assertParam(value);
return value.trim();
};
this.func__Togglebit = function(x, y) {
_assertNumber(x, 1);
_assertNumber(y, 2);
var mask = 1 << y;
return x ^ mask;
};
@ -1143,19 +1244,25 @@ var QB = new function() {
// QB45 Keywords
// --------------------------------------------
this.func_Abs = function(value) {
_assertNumber(value);
return Math.abs(value);
};
this.func_Asc = function(value, pos) {
_assertParam(value);
if (pos == undefined) {
pos = 0;
}
else { pos--; }
else { p
_assertNumber(pos, 2);
os--;
}
return String(value).charCodeAt(pos);
}
this.func_Atn = function(value) {
_assertNumber(value);
return Math.atan(value);
};
@ -1172,6 +1279,7 @@ var QB = new function() {
};
this.sub_ChDir = function(path) {
_assertParam(path);
var node = GX.vfs().getNode(path, GX.vfsCwd());
if (node) {
GX.vfsCwd(node);
@ -1182,6 +1290,7 @@ var QB = new function() {
};
this.func_Chr = function(charCode) {
_assertNumber(charCode);
return String.fromCharCode(charCode);
};
@ -1242,12 +1351,12 @@ var QB = new function() {
return _rgb(0,0,0);
}
this.sub_Color = function(x, y) {
this.sub_Color = function(fg, bg) {
if (x != undefined) {
_fgColor = _color(x);
_fgColor = _color(fg);
}
if (y != undefined) {
_bgColor = _color(y);
_bgColor = _color(bg);
}
};
@ -1256,6 +1365,7 @@ var QB = new function() {
};
this.func_Cos = function(value) {
_assertNumber(value);
return Math.cos(value);
};
@ -1264,6 +1374,7 @@ var QB = new function() {
};
this.func_Cvi = function(numString) {
_assertParam(numString);
var result = 0;
numString = numString.split("").reverse().join("");
for (let i=1;i>=0;i--) {
@ -1273,6 +1384,7 @@ var QB = new function() {
};
this.func_Cvl = function(numString) {
_assertParam(numString);
var result = 0;
numString = numString.split("").reverse().join("");
for (let i=3;i>=0;i--) {
@ -1290,7 +1402,7 @@ var QB = new function() {
};
this.sub_Draw = function(t) {
_assertParam(t);
// Turn input string into array of characters.
var u = t.toString();
u = u.replace(/\s+/g, '');
@ -1604,14 +1716,17 @@ var QB = new function() {
};
this.sub_Error = function(errorNumber) {
_assertNumber(errorNumber);
throw new Error("Unhandled Error #" + errorNumber);
};
this.func_Exp = function(value) {
_assertNumber(value);
return Math.exp(value);
};
this.func_Fix = function(value) {
_assertNumber(value);
if (value >=0) {
return Math.floor(value);
}
@ -1632,6 +1747,7 @@ var QB = new function() {
}
this.func_Hex = function(n) {
_assertNumber(n);
return n.toString(16).toUpperCase();
};
@ -1801,6 +1917,8 @@ var QB = new function() {
}
this.func_InStr = function(arg1, arg2, arg3) {
_assertParam(arg1, 1);
_assertParam(arg2, 2);
var startIndex = 0;
var strSource = "";
var strSearch = "";
@ -1817,18 +1935,23 @@ var QB = new function() {
};
this.func_Int = function(value) {
_assertNumber(value);
return Math.floor(value);
};
this.func_LCase = function(value) {
_assertParam(value);
return String(value).toLowerCase();
};
this.func_Left = function(value, n) {
_assertParam(value, 1);
_assertNumber(n, 2);
return String(value).substring(0, n);
};
this.func_Len = function(value) {
_assertParam(value);
return String(value).length;
};
@ -1841,10 +1964,12 @@ var QB = new function() {
};
this.func_Log = function(value) {
_assertNumber(value);
return Math.log(value);
};
this.func_Cdbl = function(value) {
_assertNumber(value);
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setFloat32(1, value);
@ -1852,6 +1977,7 @@ var QB = new function() {
};
this.func_Cint = function(value) {
_assertNumber(value);
if (value > 0) {
return Math.round(value);
} else {
@ -1860,6 +1986,7 @@ var QB = new function() {
};
this.func_Clng = function(value) {
_assertNumber(value);
if (value > 0) {
return Math.round(value);
} else {
@ -2098,7 +2225,9 @@ var QB = new function() {
};
this.sub_Locate = function(row, col) {
// TODO: implement cursor positioning/display
// TODO: implement cursor positioning/display parameters
if (row == undefined) { row = 1; } else { _assertNumber(row); }
if (col == undefined) { col = 1; } else { _assertNumber(col); }
if (row && row > 0 && row <= _textRows()) {
_locY = row-1;
}
@ -2109,23 +2238,28 @@ var QB = new function() {
};
this.func_LTrim = function(value) {
_assertParam(value);
return String(value).trimStart();
};
this.sub_Kill = function(path) {
_assertParam(path);
GX.vfs().removeFile(path, GX.vfsCwd());
};
this.func_Mid = function(value, n, len) {
if (len == undefined) {
return String(value).substring(n-1);
}
else {
return String(value).substring(n-1, n+len-1);
}
_assertParam(value, 1);
_assertNumber(n, 2);
if (len == undefined) {
return String(value).substring(n-1);
}
else {
return String(value).substring(n-1, n+len-1);
}
};
this.sub_MkDir = function(path) {
_assertParam(path);
var vfs = GX.vfs();
var vfsCwd = GX.vfsCwd();
var parent = vfs.getParentPath(path);
@ -2136,6 +2270,7 @@ var QB = new function() {
}
this.func_Mki = function(num) {
_assertNumber(num);
var ascii = "";
for (var i=1; i >= 0; i--) {
ascii += String.fromCharCode((num>>(8*i))&255);
@ -2144,6 +2279,7 @@ var QB = new function() {
};
this.func_Mkl = function(num) {
_assertNumber(num);
var ascii = "";
for (var i=3; i >= 0; i--) {
ascii += String.fromCharCode((num>>(8*i))&255);
@ -2152,6 +2288,8 @@ var QB = new function() {
};
this.sub_Name = function(oldName, newName) {
_assertParam(oldName, 1);
_assertParam(newName, 2);
var vfs = GX.vfs();
var vfsCwd = GX.vfsCwd();
var node = vfs.getNode(oldName, vfsCwd);
@ -2159,6 +2297,7 @@ var QB = new function() {
};
this.func_Oct = function(n) {
_assertNumber(n);
return n.toString(8).toUpperCase();
};
@ -2888,10 +3027,15 @@ var QB = new function() {
_readCursorPosition = 0;
} else {
_readCursorPosition = _dataLabelMap[t];
if (_readCursorPosition == undefined) {
throw new Error("Label '" + t + "' not defined");
}
}
};
this.func_Right = function(value, n) {
_assertParam(value, 1);
_assertNumber(n, 2);
if (value == undefined) {
return "";
}
@ -2900,6 +3044,7 @@ var QB = new function() {
};
this.func_RTrim = function(value) {
_assertParam(value);
return String(value).trimEnd();
}
@ -2925,6 +3070,7 @@ var QB = new function() {
};
this.sub_RmDir = function(path) {
_assertParam(path);
vfs.removeDirectory(path, vfsCwd);
};
@ -3036,16 +3182,20 @@ var QB = new function() {
};
this.func_Sgn = function(value) {
_assertNumber(value);
if (value > 0) { return 1; }
else if (value < 0) { return -1; }
else { return 0; }
};
this.func_Space = function(ccount) {
_assertNumber(ccount);
return QB.func_String(ccount, " ");
}
this.func_String = function(ccount, s) {
_assertNumber(ccount, 1);
_assertParam(s, 2);
if (typeof s === "string") {
s = s.substring(0, 1);
}
@ -3056,6 +3206,7 @@ var QB = new function() {
};
this.func_Sin = function(value) {
_assertNumber(value);
return Math.sin(value);
};
@ -3063,7 +3214,10 @@ var QB = new function() {
var elapsed = 0;
var totalWait = Infinity;
if (seconds != undefined) {
totalWait = seconds*1000;
_assertNumber(seconds);
if (seconds > 0) {
totalWait = seconds*1000;
}
}
_lastKey = null;
@ -3076,6 +3230,8 @@ var QB = new function() {
this.sub_Sound = async function(freq, duration, shape, decay, gain) {
_assertNumber(freq, 1);
_assertNumber(duration, 2);
if (shape == undefined || (typeof shape != 'string')) { shape = "square"; }
if (decay == undefined || (typeof decay != 'number')) { decay = 0.0; }
if (gain == undefined || (typeof gain != 'number')) { gain = 1.0; }
@ -3106,6 +3262,7 @@ var QB = new function() {
};
this.func_Sqr = function(value) {
_assertNumber(value);
return Math.sqrt(value);
};
@ -3120,6 +3277,7 @@ var QB = new function() {
};
this.func_Tan = function(value) {
__assertNumber(value);
return Math.tan(value);
};
@ -3140,24 +3298,34 @@ var QB = new function() {
};
this.func_LBound = function(a, dimension) {
_assertParam(a);
if (dimension == undefined) {
dimension = 1;
}
else {
_assertNumber(dimension, 2);
}
return a._dimensions[dimension-1].l;
};
this.func_UBound = function(a, dimension) {
_assertParam(a);
if (dimension == undefined) {
dimension = 1;
}
else {
_assertNumber(dimension, 2);
}
return a._dimensions[dimension-1].u;
};
this.func_UCase = function(value) {
_assertParam(value);
return String(value).toUpperCase();
};
this.func_Val = function(value) {
_assertParam(value);
var ret;
value = value.toString();
if (value.substring(0, 2) == "&H") {

505
qb2js.js

File diff suppressed because it is too large Load diff

View file

@ -264,6 +264,14 @@ var IDE = new function() {
await displayWarnings();
if (_hasError()) {
consoleVisible = true;
window.onresize();
QB.halt();
GX.sceneStop();
return false;
}
_e.jsCode.innerHTML = jsCode;
window.onresize();
@ -301,6 +309,16 @@ var IDE = new function() {
return false;
}
function _hasError() {
var warnings = QBCompiler.getWarnings();
for (var i=0; i < warnings.length; i++) {
if (warnings[i].mtype == 1) {
return true;
}
}
return false;
}
function _stopProgram() {
QB.halt();
GX.sceneStop();
@ -589,7 +607,8 @@ var IDE = new function() {
var td1 = document.createElement("td");
var td2 = document.createElement("td");
var td3 = document.createElement("td");
_addWarningCell(tr, "WARN");
var mtype = (w[i].mtype == 1) ? "ERROR": "WARN";
_addWarningCell(tr, mtype);
_addWarningCell(tr, ":");
_addWarningCell(tr, w[i].line);
_addWarningCell(tr, ":");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View file

@ -2,9 +2,13 @@ Export Render
IncludeJS "https://cdnjs.cloudflare.com/ajax/libs/camanjs/4.0.0/caman.full.min.js"
Sub Render(opts)
Dim lastOpts As Object
Sub Render(opts, imageId)
$If Javascript Then
Caman("#gx-canvas", function() {
var complete = false;
if (imageId == undefined) { imageId = 0; }
Caman(QB.getImage(imageId), function() {
if (opts.brightness) { this.brightness(opts.brightness); }
if (opts.contrast) { this.contrast(opts.contrast); }
if (opts.saturation) { this.saturation(opts.saturation); }
@ -18,6 +22,9 @@ $If Javascript Then
if (opts.sharpen) { this.sharpen(opts.sharpen); }
if (opts.blur) { this.stackBlur(opts.blur); }
this.render();
lastOpts = opts;
complete = true;
});
while (!complete) { await GX.sleep(10); }
$End If
End Sub

View file

@ -6,8 +6,8 @@ $Console:Only
'2) Compile to EXE only.
'3) In console, run: qb2js qb2js.bas > ../qb2js.js
Const FILE = 1
Const TEXT = 2
Const FILE = 1, TEXT = 2
Const MWARNING = 0, MERROR = 1
Const False = 0
Const True = Not False
Const PrintDataTypes = True
@ -18,6 +18,7 @@ Const PrintTokenizedLine = False
Type CodeLine
line As Integer
text As String
mtype As Integer
End Type
Type Method
@ -59,10 +60,11 @@ Type Label
index As Integer
End Type
Type LoopItem
Type Container
mode As Integer
type As String
label As String
line As Integer
End Type
ReDim Shared As CodeLine lines(0)
@ -181,7 +183,8 @@ Sub QBToJS (source As String, sourceType As Integer, moduleName As String)
AddJSLine 0, " for (var i=1; i <= QB.func_UBound(warnings); i++) {"
AddJSLine 0, " w.push({"
AddJSLine 0, " line: QB.arrayValue(warnings, [i]).value.line,"
AddJSLine 0, " text: QB.arrayValue(warnings, [i]).value.text"
AddJSLine 0, " text: QB.arrayValue(warnings, [i]).value.text,"
AddJSLine 0, " mtype: QB.arrayValue(warnings, [i]).value.mtype"
AddJSLine 0, " });"
AddJSLine 0, " }"
AddJSLine 0, " return w;"
@ -294,12 +297,13 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
Dim totalIndent As Integer
totalIndent = 1
Dim caseCount As Integer
Dim loopMode(100) As LoopItem ' TODO: only supports 100 levels of do/loop nesting
Dim loopLevel As Integer
Dim containers(10000) As Container ' TODO: replace hardcoded limit?
Dim cindex As Integer
Dim caseVar As String
Dim currType As Integer
Dim loopIndex As String
Dim sfix As String
Dim ctype As String
For i = firstLine To lastLine
indent = 0
@ -350,6 +354,8 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
DeclareTypeVar parts(), currType, i
End If
Else
CheckParen lines(i).text, i
If first = "CONST" Then
ReDim As String constParts(0)
Dim As Integer constCount
@ -374,6 +380,10 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
ElseIf first = "SELECT" Then
cindex = cindex + 1
containers(cindex).type = "SELECT CASE"
containers(cindex).line = i
caseVar = GenJSVar
js = "var " + caseVar + " = " + ConvertExpression(Join(parts(), 3, -1, " "), i) + "; "
js = js + "switch (" + caseVar + ") {"
@ -426,12 +436,13 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
If Left$(_Trim$(fstep), 1) = "-" Then fcond = " >= "
loopLevel = loopLevel + 1
loopMode(loopLevel).type = "FOR"
loopMode(loopLevel).label = GenJSLabel
cindex = cindex + 1
containers(cindex).type = "FOR"
containers(cindex).label = GenJSLabel
containers(cindex).line = i
loopIndex = GenJSVar
js = "var " + loopIndex + " = 0; " + loopMode(loopLevel).label + ":"
js = "var " + loopIndex + " = 0; " + containers(cindex).label + ":"
js = js + " for (" + fvar + "=" + sval + "; " + fvar + fcond + uval + "; " + fvar + "=" + fvar + " + " + fstep + ") {"
js = js + " if (QB.halted()) { return; } "
js = js + loopIndex + "++; "
@ -440,6 +451,10 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
indent = 1
ElseIf first = "IF" Then
cindex = cindex + 1
containers(cindex).type = "IF"
containers(cindex).line = i
Dim thenIndex As Integer
For thenIndex = 2 To UBound(parts)
If UCase$(parts(thenIndex)) = "THEN" Then Exit For
@ -463,21 +478,42 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
Dim npi As Integer
npcount = ListSplit(Join(parts(), 2, -1, " "), nparts())
For npi = 1 To npcount
js = js + "} "
indent = indent - 1
If CheckBlockEnd(containers(), cindex, first, i) Then
js = js + "} "
indent = -1
cindex = cindex - 1
Else
Exit For
End If
Next npi
Else
js = js + "}"
indent = -1
If CheckBlockEnd(containers(), cindex, first, i) Then
js = js + "}"
indent = -1
cindex = cindex - 1
End If
End If
ElseIf first = "END" Then
If UBound(parts) = 1 Then
js = "QB.halt(); return;"
Else
If UCase$(parts(2)) = "SELECT" Then js = "break;"
js = js + "}"
indent = -1
second = UCase$(parts(2))
If second = "IF" Then
If CheckBlockEnd(containers(), cindex, "END IF", i) Then
js = js + "}"
indent = -1
cindex = cindex - 1
End If
ElseIf second = "SELECT" Then
If CheckBlockEnd(containers(), cindex, "END SELECT", i) Then
js = "break;" + " }"
indent = -1
cindex = cindex - 1
End If
Else
AddError i, "Syntax error after END"
End If
End If
ElseIf first = "SYSTEM" Then
@ -494,12 +530,13 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
End If
ElseIf first = "DO" Then
loopLevel = loopLevel + 1
loopMode(loopLevel).label = GenJSLabel
loopMode(loopLevel).type = "DO"
cindex = cindex + 1
containers(cindex).label = GenJSLabel
containers(cindex).type = "DO"
containers(cindex).line = i
loopIndex = GenJSVar
js = "var " + loopIndex + " = 0; " + loopMode(loopLevel).label + ":"
js = "var " + loopIndex + " = 0; " + containers(cindex).label + ":"
If UBound(parts) > 1 Then
sfix = FixCondition(UCase$(parts(2)), parts(), 2, "DO ")
@ -509,10 +546,10 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
Else
js = js + " while (!(" + ConvertExpression(Join(parts(), 3, -1, " "), i) + ")) {"
End If
loopMode(loopLevel).mode = 1
containers(cindex).mode = 1
Else
js = js + " do {"
loopMode(loopLevel).mode = 2
containers(cindex).mode = 2
End If
indent = 1
js = js + " if (QB.halted()) { return; }"
@ -521,12 +558,13 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
ElseIf first = "WHILE" Then
loopLevel = loopLevel + 1
loopMode(loopLevel).label = GenJSLabel
loopMode(loopLevel).type = "WHILE"
cindex = cindex + 1
containers(cindex).label = GenJSLabel
containers(cindex).type = "WHILE"
containers(cindex).line = i
loopIndex = GenJSVar
js = "var " + loopIndex + " = 0; " + loopMode(loopLevel).label + ":"
js = "var " + loopIndex + " = 0; " + containers(cindex).label + ":"
js = js + " while (" + ConvertExpression(Join(parts(), 2, -1, " "), i) + ") {"
js = js + " if (QB.halted()) { return; }"
js = js + loopIndex + "++; "
@ -535,26 +573,35 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
indent = 1
ElseIf first = "WEND" Then
js = "}"
loopLevel = loopLevel - 1
indent = -1
'ctype = ""
'If cindex > 0 Then ctype = containers(cindex).type
'If ctype <> "WHILE" Then
' AddWarning i, "WEND without WHILE"
'Else
If CheckBlockEnd(containers(), cindex, first, i) Then
js = "}"
cindex = cindex - 1
indent = -1
End If
ElseIf first = "LOOP" Then
If loopMode(loopLevel).mode = 1 Then
js = "}"
Else
sfix = FixCondition(UCase$(parts(2)), parts(), 2, "LOOP ")
js = "} while (("
If UBound(parts) < 2 Then
js = js + "1));"
If CheckBlockEnd(containers(), cindex, first, i) Then
If containers(cindex).mode = 1 Then
js = "}"
Else
If UCase$(parts(2)) = "UNTIL" Then js = "} while (!("
js = js + ConvertExpression(Join(parts(), 3, UBound(parts), " "), i) + "))"
sfix = FixCondition(UCase$(parts(2)), parts(), 2, "LOOP ")
js = "} while (("
If UBound(parts) < 2 Then
js = js + "1));"
Else
If UCase$(parts(2)) = "UNTIL" Then js = "} while (!("
js = js + ConvertExpression(Join(parts(), 3, UBound(parts), " "), i) + "))"
End If
End If
cindex = cindex - 1
indent = -1
End If
loopLevel = loopLevel - 1
indent = -1
ElseIf first = "_CONTINUE" Or first = "CONTINUE" Then
js = "continue;"
@ -571,19 +618,19 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
ElseIf second = "DO" Or second = "WHILE" Or second = "FOR" Then
Dim lli As Integer
For lli = loopLevel To 0 Step -1
For lli = cindex To 0 Step -1
If lli > 0 Then
If loopMode(lli).type = second Then Exit For
If containers(lli).type = second Then Exit For
End If
Next lli
If lli > 0 Then
js = "break " + loopMode(lli).label + ";"
js = "break " + containers(lli).label + ";"
Else
AddWarning i, "EXIT " + second + " without " + second + " on current line"
AddError i, "EXIT " + second + " without " + second
End If
Else
AddWarning i, "Syntax error after EXIT"
AddError i, "Syntax error after EXIT"
End If
ElseIf first = "TYPE" Then
@ -697,8 +744,50 @@ Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As Str
Next i
If cindex > 0 Then
AddError containers(cindex).line, containers(cindex).type + " without " + EndPhraseFor(containers(cindex).type)
End If
End Sub
Function BeginPhraseFor$ (endPhrase As String)
Dim bp As String
Select Case endPhrase
Case "NEXT": bp = "FOR"
Case "LOOP": bp = "DO"
Case "WEND": bp = "WHILE"
Case "END IF": bp = "IF"
Case "END SELECT": bp = "SELECT CASE"
End Select
BeginPhraseFor = bp
End Function
Function EndPhraseFor$ (beginPhrase As String)
Dim ep As String
Select Case beginPhrase
Case "FOR": ep = "NEXT"
Case "DO": ep = "LOOP"
Case "WHILE": ep = "WEND"
Case "IF": ep = "END IF"
Case "SELECT CASE": ep = "END SELECT"
End Select
EndPhraseFor = ep
End Function
Function CheckBlockEnd (cstack() As Container, cindex As Integer, endPhrase As String, lineNumber As Integer)
Dim As String ctype, beginPhrase
Dim success As Integer
success = True
beginPhrase = BeginPhraseFor(endPhrase)
If cindex > 0 Then ctype = cstack(cindex).type
If ctype <> beginPhrase Then
AddError lineNumber, endPhrase + " without " + beginPhrase
success = False
End If
CheckBlockEnd = success
End Function
Function FixCondition$ (word As String, parts() As String, idx As Integer, prefix As String)
' The fact that we are doing this probably means we need to improve the initial "tokenizer"
' Is this is a condition keyword with no space between the keyword and the open paren?
@ -728,7 +817,7 @@ Sub ParseExport (s As String, lineIndex As Integer)
Dim c As Integer
c = SLSplit(s, parts(), False)
AddWarning lineIndex, "ParseExport: [" + s + "]"
'AddWarning lineIndex, "ParseExport: [" + s + "]"
If FindMethod(parts(1), es, "SUB") Then
If c > 2 Then
@ -1924,9 +2013,9 @@ Function ConvertExpression$ (ex As String, lineNumber As Integer)
If uword = "NOT" Then
js = js + "!"
ElseIf uword = "AND" Then
js = js + " && "
js = js + " & "
ElseIf uword = "OR" Then
js = js + " || "
js = js + " | "
ElseIf uword = "MOD" Then
js = js + " % "
ElseIf uword = "XOR" Then
@ -2657,6 +2746,35 @@ Function FindOperator (c As String, c2 As String)
End If
End Function
Sub CheckParen (sourceString As String, lineNumber As Long)
Dim i As Integer
Dim quoteMode As Integer
Dim paren As Integer
For i = 1 To Len(sourceString)
Dim c As String
c = Mid$(sourceString, i, 1)
If c = Chr$(34) Then
quoteMode = Not quoteMode
ElseIf quoteMode Then
' skip the remaining checks and move to the next char
ElseIf c = "(" Then
paren = paren + 1
ElseIf c = ")" Then
paren = paren - 1
End If
Next i
If paren < 0 Then
AddError lineNumber, "Missing ("
ElseIf paren > 0 Then
AddError lineNumber, "Missing )"
End If
End Sub
' String literal-aware split - copy
Function SLSplit2 (sourceString As String, results() As String)
Dim cstr As String
@ -3000,6 +3118,10 @@ Sub AddWarning (sourceLine As Integer, msgText As String)
warnings(lcount).text = msgText
End Sub
Sub AddError (sourceLine As Integer, msgText As String)
AddWarning sourceLine, msgText
warnings(UBound(warnings)).mtype = MERROR
End Sub
Sub AddConst (vname As String)
Dim v As Variable