1
0
mirror of https://github.com/pyscript/pyscript.git synced 2022-05-01 19:47:48 +03:00

merge main

This commit is contained in:
Fabio Pliger
2022-04-10 15:57:04 -05:00
73 changed files with 230 additions and 476 deletions

View File

@@ -0,0 +1,51 @@
<html><head>
<title>Bokeh Example</title>
<meta charset="iso-8859-1">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.2.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<py-env>
- bokeh
- numpy
</py-env>
<h1>Bokeh Example</h1>
<div id="myplot"></div>
<py-script>
import json
import pyodide
from js import Bokeh, console, JSON
from bokeh.embed import json_item
from bokeh.plotting import figure
from bokeh.resources import CDN
# create a new plot with default tools, using figure
p = figure(plot_width=400, plot_height=400)
# add a circle renderer with x and y coordinates, size, color, and alpha
p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=15, line_color="navy", fill_color="orange", fill_alpha=0.5)
p_json = json.dumps(json_item(p, "myplot"))
print("about to embed")
Bokeh.embed.embed_item(JSON.parse(p_json))
print ("Done embedding...")
</py-script>
</body>
</html>

View File

@@ -0,0 +1,98 @@
<html><head>
<title>Bokeh Example</title>
<meta charset="iso-8859-1">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.2.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<py-env>
- bokeh
- numpy
</py-env>
<h1>Bokeh Example</h1>
<div id="myplot"></div>
<py-script>
import asyncio
import json
import pyodide
from js import Bokeh, console, JSON
from bokeh import __version__
from bokeh.document import Document
from bokeh.embed.util import OutputDocumentFor, standalone_docs_json
from bokeh.models import Slider, Div
from bokeh.layouts import Row
from bokeh.protocol.messages.patch_doc import process_document_events
# create a new plot with default tools, using figure
p = Slider(start=0.1, end=10, value=1, step=.1, title="Amplitude")
div = Div(text=f'Amplitude is: {p.value}')
def callback(attr, old, new):
div.text = f'Amplitude is: {new}'
p.on_change('value', callback)
row = Row(children=[p, div])
print("about to embed")
def doc_json(model, target):
with OutputDocumentFor([model]) as doc:
doc.title = ""
docs_json = standalone_docs_json([model])
doc_json = list(docs_json.values())[0]
root_id = doc_json['roots']['root_ids'][0]
return doc, json.dumps(dict(
target_id = target,
root_id = root_id,
doc = doc_json,
version = __version__,
))
def _link_docs(pydoc, jsdoc):
def jssync(event):
if getattr(event, 'setter_id', None) is not None:
return
events = [event]
json_patch = jsdoc.create_json_patch_string(pyodide.to_js(events))
pydoc.apply_json_patch(json.loads(json_patch))
jsdoc.on_change(pyodide.create_proxy(jssync), pyodide.to_js(False))
def pysync(event):
json_patch, buffers = process_document_events([event], use_buffers=True)
buffer_map = {}
for (ref, buffer) in buffers:
buffer_map[ref['id']] = buffer
jsdoc.apply_json_patch(JSON.parse(json_patch), pyodide.to_js(buffer_map), setter_id='js')
pydoc.on_change(pysync)
async def show(plot, target):
pydoc, model_json = doc_json(plot, target)
views = await Bokeh.embed.embed_item(JSON.parse(model_json))
print("Done embedding...")
jsdoc = views[0].model.document
_link_docs(pydoc, jsdoc)
await show(row, 'myplot')
</py-script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../../build/pyscript.css" />
<script defer src="../../build/pyscript.js"></script>
</head>
<body>
<py-script>
from js import handTrack, setTimeout, requestAnimationFrame
from pyodide import create_once_callable
import asyncio
context = canvas.element.getContext("2d")
isVideo = False
model = None
modelParams = {
"flipHorizontal": True, # flip e.g for video
"maxNumBoxes": 20, # maximum number of boxes to detect
"iouThreshold": 0.5, # ioU threshold for non-max suppression
"scoreThreshold": 0.6, # confidence threshold for predictions.
}
def toggle_video(evt):
global isVideo
if (not isVideo):
update_note.write("Starting video")
pyscript.run_until_complete(start_video())
else:
update_note.write("Stopping video")
handTrack.stopVideo(video.element)
isVideo = False
update_note.write("Video stopped")
async def start_video():
global isVideo
update_note.write("Inside sstart video")
status = await handTrack.startVideo(video.element)
console.log("video started", status)
if status:
update_note.write("Video started. Now tracking")
isVideo = True
console.log( "Calling RUN DETECTION")
y = await run_detection()
else:
update_note.write( "Please enable video")
def sync_run_detection(evt):
pyscript.run_until_complete(run_detection())
async def run_detection():
console.log("in RUN DETECTION: ");
global model
global isVideo
console.log("...1")
predictions = await model.detect(video.element)
console.log("done...1")
console.log("Predictions: ", predictions);
model.renderPredictions(predictions, canvas.element, context, video.element);
console.log("is Video?", isVideo)
if (isVideo):
console.log("requestingAnimation!")
await requestAnimationFrame(create_once_callable(sync_run_detection));
console.log("...2")
def run_detection_image(img):
console.log("in RUN DETECTION IMAGE", predictions);
global model
def detect(predition):
console.log("Predictions: ", predictions);
model.renderPredictions(predictions, canvas, context, img);
console.log("...3")
model.detect(img).then(detect)
console.log("...4")
def handle_model(lmodel):
global model
model = lmodel
update_note.write("Loaded Model!")
async def start():
console.log("creating x")
console.log("calling x")
model = await handTrack.load(modelParams)#.then(handle_model)
console.log("loaded model!")
console.log(model)
handle_model(model)
print(dir(x))
print(x)
pyscript.run_until_complete(start())
#});
</py-script>
<div class="mb10">
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" pys-onClick="toggle_video">
Toggle Video
</button>
<button id="nextimagebutton" class="mt10 bx--btn bx--btn--secondary" type="button" disabled>
Next Image
</button>
<div id="update-note" py-mount class="updatenote mt10">loading model ..</div>
</div>
<div>
<video autoplay="autoplay" id="myvideo" py-mount="video"></video>
<canvas id="canvas" py-mount class="border canvasbox"></canvas>
</div>
<script src="lib/handtrack.min.js"> </script>
</html>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>PyScript demo</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="build/bundle.css" />
<script defer src="build/bundle.js"></script>
</head>
<body class="container">
<h1 class="text-3xl font-bold">PyScript demos</h1>
<hr/>
<h2 class="text-2xl font-bold text-blue-600"><a href="./simple_script.html">Simple script</a></h2>
<p>A static demo of the <code>&lt;py-script&gt;</code> tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./simple_script2.html">Simple script 2</a></h2>
<p>A dynamic demo of the <code>&lt;py-script&gt;</code> tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./repl.html">REPL</a></h2>
<p>A Python REPL (Read Eval Print Loop). </p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./repl2.html">REPL2</a></h2>
<p>A Python REPL (Read Eval Print Loop) with slightly better formatting.</p>
</body>
</html>

View File

@@ -0,0 +1,32 @@
html, body, ul, li {
margin: 0;
border: 0;
padding: 0;
}
canvas {
display: block;
width: 762;
margin: 0 auto;
background-color: blue;
}
p {
text-align: center;
}
body {
overflow: hidden;
height: 100%;
}
html {
overflow: hidden;
height: 100%;
}
.info {
position: absolute;
top: 0;
left: 0;
}

View File

@@ -0,0 +1,45 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Bcoin = Mario.Bcoin = function(pos) {
Mario.Entity.call(this, {
pos: pos,
sprite: level.bcoinSprite(),
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Bcoin, Mario.Entity);
//I'm not sure whether it makes sense to use an array for vel and acc here
//in order to keep with convention, or to just use a single value, since
//it's literally impossible for these to move left or right.
Bcoin.prototype.spawn = function() {
sounds.coin.currentTime = 0.05;
sounds.coin.play();
this.idx = level.items.length;
level.items.push(this);
this.active = true;
this.vel = -12;
this.targetpos = this.pos[1] - 32;
}
Bcoin.prototype.update = function(dt) {
if (!this.active) return;
if (this.vel > 0 && this.pos[1] >= this.targetpos) {
player.coins += 1;
//spawn a score thingy.
delete level.items[this.idx];
}
this.acc = 0.75;
this.vel += this.acc;
this.pos[1] += this.vel;
this.sprite.update(dt);
}
Bcoin.prototype.checkCollisions = function() {;}
})();

View File

@@ -0,0 +1,81 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//TODO: clean up the logic for sprite switching.
//TODO: There's a weird bug with the collision logic. Look into it.
var Block = Mario.Block = function(options) {
this.item = options.item;
this.usedSprite = options.usedSprite;
this.bounceSprite = options.bounceSprite;
this.breakable = options.breakable;
Mario.Entity.call(this, {
pos: options.pos,
sprite: options.sprite,
hitbox: [0,0,16,16]
});
this.standing = true;
}
Mario.Util.inherits(Block, Mario.Floor);
Block.prototype.break = function() {
sounds.breakBlock.play();
(new Mario.Rubble()).spawn(this.pos);
var x = this.pos[0] / 16, y = this.pos[1] / 16;
delete level.blocks[y][x];
}
Block.prototype.bonk = function(power) {
sounds.bump.play();
if (power > 0 && this.breakable) {
this.break();
} else if (this.standing){
this.standing = false;
if (this.item) {
this.item.spawn();
this.item = null;
}
this.opos = [];
this.opos[0] = this.pos[0];
this.opos[1] = this.pos[1];
if (this.bounceSprite) {
this.osprite = this.sprite;
this.sprite = this.bounceSprite;
} else {
this.sprite = this.usedSprite;
}
this.vel[1] = -2;
}
}
Block.prototype.update = function(dt, gameTime) {
if (!this.standing) {
if (this.pos[1] < this.opos[1] - 8) {
this.vel[1] = 2;
}
if (this.pos[1] > this.opos[1]) {
this.vel[1] = 0;
this.pos = this.opos;
if (this.osprite) {
this.sprite = this.osprite;
}
this.standing = true;
}
} else {
if (this.sprite === this.usedSprite) {
var x = this.pos[0] / 16, y = this.pos[1] / 16;
level.statics[y][x] = new Mario.Floor(this.pos, this.usedSprite);
delete level.blocks[y][x];
}
}
this.pos[1] += this.vel[1];
this.sprite.update(dt, gameTime);
}
})();

View File

@@ -0,0 +1,47 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Coin = Mario.Coin = function(pos, sprite) {
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [0,0,16,16]
});
this.idx = level.items.length
}
Mario.Util.inherits(Coin, Mario.Entity);
Coin.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
this.collect();
}
}
}
Coin.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
//money is not affected by gravity, you see.
Coin.prototype.update = function(dt) {
this.sprite.update(dt);
}
Coin.prototype.checkCollisions = function() {
this.isPlayerCollided();
}
Coin.prototype.collect = function() {
sounds.coin.currentTime = 0.05;
sounds.coin.play();
player.coins += 1;
delete level.items[this.idx]
}
})();

View File

@@ -0,0 +1,34 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Entity = Mario.Entity = function(options) {
this.vel = [0,0];
this.acc = [0,0];
this.standing = true;
this.pos = options.pos;
this.sprite = options.sprite;
this.hitbox = options.hitbox;
this.left = false;
}
Entity.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY)
}
Entity.prototype.collideWall = function(wall) {
//the wall will always be a 16x16 block with hitbox = [0,0,16,16].
if (this.pos[0] > wall.pos[0]) {
//from the right
this.pos[0] = wall.pos[0] + wall.hitbox[2] - this.hitbox[0];
this.vel[0] = Math.max(0, this.vel[0]);
this.acc[0] = Math.max(0, this.acc[0]);
} else {
this.pos[0] = wall.pos[0] + wall.hitbox[0] - this.hitbox[2] - this.hitbox[0];
this.vel[0] = Math.min(0, this.vel[0]);
this.acc[0] = Math.min(0, this.acc[0]);
}
}
Entity.prototype.bump = function() {;}
})();

View File

@@ -0,0 +1,126 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Fireball = Mario.Fireball = function(pos) {
this.hit = 0;
this.standing = false;
Mario.Entity.call(this, {
pos: pos,
sprite: new Mario.Sprite('sprites/items.png', [96, 144], [8,8], 5, [0,1,2,3]),
hitbox: [0,0,8,8]
});
}
Mario.Util.inherits(Fireball, Mario.Entity);
Fireball.prototype.spawn = function(left) {
sounds.fireball.currentTime = 0;
sounds.fireball.play();
if (fireballs[0]) {
this.idx = 1;
fireballs[1] = this;
} else {
this.idx = 0;
fireballs[0] = this;
}
this.vel[0] = (left ? -5 : 5);
this.standing = false;
this.vel[1] = 0;
}
Fireball.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Fireball.prototype.update = function(dt) {
if (this.hit == 1) {
this.sprite.pos = [96, 160];
this.sprite.size = [16,16];
this.sprite.frames = [0,1,2];
this.sprite.speed = 8;
this.hit += 1;
return;
} else if (this.hit == 5) {
delete fireballs[this.idx];
player.fireballs -= 1;
return;
} else if (this.hit) {
this.hit += 1;
return;
}
//In retrospect, the way collision is being handled is RIDICULOUS
//but I don't have to use some horrible kludge for this.
if (this.standing) {
this.standing = false;
this.vel[1] = -4;
}
this.acc[1] = 0.5;
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
if (this.pos[0] < vX || this.pos[0] > vX + 256) {
this.hit = 1;
}
this.sprite.update(dt);
}
Fireball.prototype.collideWall = function() {
if (!this.hit) this.hit = 1;
}
Fireball.prototype.checkCollisions = function() {
if (this.hit) return;
var h = this.pos[1] % 16 < 8 ? 1 : 2;
var w = this.pos[0] % 16 < 8 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete fireballs[this.idx];
player.fireballs -= 1;
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
var that = this;
level.enemies.forEach(function(enemy){
if (enemy.flipping || enemy.pos[0] - vX > 336){ //stop checking once we get to far away dudes.
return;
} else {
that.isCollideWith(enemy);
}
});
}
Fireball.prototype.isCollideWith = function(ent) {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [ent.pos[0] + ent.hitbox[0], ent.pos[1] + ent.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
this.hit = 1;
ent.bump();
}
}
};
Fireball.prototype.bump = function() {;}
})();

View File

@@ -0,0 +1,74 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Fireflower = Mario.Fireflower = function(pos) {
this.spawning = false;
this.waiting = 0;
Mario.Entity.call(this, {
pos: pos,
sprite: level.fireFlowerSprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Fireflower, Mario.Entity);
Fireflower.prototype.render = function(ctx, vX, vY) {
if (this.spawning > 1) return;
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Fireflower.prototype.spawn = function() {
sounds.itemAppear.play();
this.idx = level.items.length;
level.items.push(this);
this.spawning = 12;
this.targetpos = [];
this.targetpos[0] = this.pos[0];
this.targetpos[1] = this.pos[1] - 16;
}
Fireflower.prototype.update = function(dt) {
if (this.spawning > 1) {
this.spawning -= 1;
if (this.spawning == 1) this.vel[1] = -.5;
return;
}
if (this.spawning) {
if (this.pos[1] <= this.targetpos[1]) {
this.pos[1] = this.targetpos[1];
this.vel[1] = 0;
this.spawning = 0;
}
}
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
}
Fireflower.prototype.checkCollisions = function() {
if (this.spawning) {return;}
this.isPlayerCollided();
}
Fireflower.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
player.powerUp(this.idx);
}
}
}
//This should never be called, but just in case.
Fireflower.prototype.bump = function() {;}
})();

View File

@@ -0,0 +1,47 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
Flag = Mario.Flag = function(pos) {
//afaik flags always have the same height and Y-position
this.pos = [pos, 49];
this.hitbox = [0,0,0,0];
this.vel = [0,0];
this.acc = [0,0];
}
Flag.prototype.collideWall = function() {;
}
Flag.prototype.update = function(dt){
if (!this.done && this.pos[1] >= 170) {
this.vel = [0,0];
this.pos[1] = 170;
player.exit();
this.done = true;
}
this.pos[1] += this.vel[1];
}
Flag.prototype.checkCollisions = function() {
this.isPlayerCollided();
}
Flag.prototype.isPlayerCollided = function() {
if (this.hit) return;
if (player.pos[0] + 8 >= this.pos[0]) {
music.overworld.pause();
sounds.flagpole.play();
setTimeout(function() {
music.clear.play();
}, 2000);
this.hit = true;
player.flag();
this.vel = [0, 2];
}
}
Flag.prototype.render = function() {
level.flagpoleSprites[2].render(ctx, this.pos[0]-8, this.pos[1], vX, vY);
}
})();

View File

@@ -0,0 +1,56 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Floor = Mario.Floor = function(pos, sprite) {
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Floor, Mario.Entity);
Floor.prototype.isCollideWith = function (ent) {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [Math.floor(this.pos[0] + this.hitbox[0]), Math.floor(this.pos[1] + this.hitbox[1])];
var hpos2 = [Math.floor(ent.pos[0] + ent.hitbox[0]), Math.floor(ent.pos[1] + ent.hitbox[1])];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
if (!this.standing) {
ent.bump();
} else {
//if the entity is over the block, it's basically floor
var center = hpos2[0] + ent.hitbox[2] / 2;
if (Math.abs(hpos2[1] + ent.hitbox[3] - hpos1[1]) <= ent.vel[1]) {
if (level.statics[(this.pos[1] / 16) - 1][this.pos[0] / 16]) {return};
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] - ent.hitbox[3] - ent.hitbox[1];
ent.standing = true;
if (ent instanceof Mario.Player) {
ent.jumping = 0;
}
} else if (Math.abs(hpos2[1] - hpos1[1] - this.hitbox[3]) > ent.vel[1] &&
center + 2 >= hpos1[0] && center - 2 <= hpos1[0] + this.hitbox[2]) {
//ent is under the block.
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] + this.hitbox[3];
if (ent instanceof Mario.Player) {
this.bonk(ent.power);
ent.jumping = 0;
}
} else {
//entity is hitting it from the side, we're a wall
ent.collideWall(this);
}
}
}
}
}
Floor.prototype.bonk = function() {;}
})();

View File

@@ -0,0 +1,241 @@
var requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
//create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
var updateables = [];
var fireballs = [];
var player = new Mario.Player([0,0]);
//we might have to get the size and calculate the scaling
//but this method should let us make it however big.
//Cool!
//TODO: Automatically scale the game to work and look good on widescreen.
//TODO: fiddling with scaled sprites looks BETTER, but not perfect. Hmm.
canvas.width = 762;
canvas.height = 720;
ctx.scale(3,3);
document.body.appendChild(canvas);
//viewport
var vX = 0,
vY = 0,
vWidth = 256,
vHeight = 240;
//load our images
resources.load([
'sprites/player.png',
'sprites/enemy.png',
'sprites/tiles.png',
'sprites/playerl.png',
'sprites/items.png',
'sprites/enemyr.png',
]);
resources.onReady(init);
var level;
var sounds;
var music;
//initialize
var lastTime;
function init() {
music = {
overworld: new Audio('sounds/aboveground_bgm.ogg'),
underground: new Audio('sounds/underground_bgm.ogg'),
clear: new Audio('sounds/stage_clear.wav'),
death: new Audio('sounds/mariodie.wav')
};
sounds = {
smallJump: new Audio('sounds/jump-small.wav'),
bigJump: new Audio('sounds/jump-super.wav'),
breakBlock: new Audio('sounds/breakblock.wav'),
bump: new Audio('sounds/bump.wav'),
coin: new Audio('sounds/coin.wav'),
fireball: new Audio('sounds/fireball.wav'),
flagpole: new Audio('sounds/flagpole.wav'),
kick: new Audio('sounds/kick.wav'),
pipe: new Audio('sounds/pipe.wav'),
itemAppear: new Audio('sounds/itemAppear.wav'),
powerup: new Audio('sounds/powerup.wav'),
stomp: new Audio('sounds/stomp.wav')
};
Mario.oneone();
lastTime = Date.now();
main();
}
var gameTime = 0;
//set up the game loop
function main() {
var now = Date.now();
var dt = (now - lastTime) / 1000.0;
update(dt);
render();
lastTime = now;
requestAnimFrame(main);
}
function update(dt) {
gameTime += dt;
handleInput(dt);
updateEntities(dt, gameTime);
checkCollisions();
}
function handleInput(dt) {
if (player.piping || player.dying || player.noInput) return; //don't accept input
if (input.isDown('RUN')){
player.run();
} else {
player.noRun();
}
if (input.isDown('JUMP')) {
player.jump();
} else {
//we need this to handle the timing for how long you hold it
player.noJump();
}
if (input.isDown('DOWN')) {
player.crouch();
} else {
player.noCrouch();
}
if (input.isDown('LEFT')) { // 'd' or left arrow
player.moveLeft();
}
else if (input.isDown('RIGHT')) { // 'k' or right arrow
player.moveRight();
} else {
player.noWalk();
}
}
//update all the moving stuff
function updateEntities(dt, gameTime) {
player.update(dt, vX);
updateables.forEach (function(ent) {
ent.update(dt, gameTime);
});
//This should stop the jump when he switches sides on the flag.
if (player.exiting) {
if (player.pos[0] > vX + 96)
vX = player.pos[0] - 96
}else if (level.scrolling && player.pos[0] > vX + 80) {
vX = player.pos[0] - 80;
}
if (player.powering.length !== 0 || player.dying) { return; }
level.items.forEach (function(ent) {
ent.update(dt);
});
level.enemies.forEach (function(ent) {
ent.update(dt, vX);
});
fireballs.forEach(function(fireball) {
fireball.update(dt);
});
level.pipes.forEach (function(pipe) {
pipe.update(dt);
});
}
//scan for collisions
function checkCollisions() {
if (player.powering.length !== 0 || player.dying) { return; }
player.checkCollisions();
//Apparently for each will just skip indices where things were deleted.
level.items.forEach(function(item) {
item.checkCollisions();
});
level.enemies.forEach (function(ent) {
ent.checkCollisions();
});
fireballs.forEach(function(fireball){
fireball.checkCollisions();
});
level.pipes.forEach (function(pipe) {
pipe.checkCollisions();
});
}
//draw the game!
function render() {
updateables = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = level.background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
//scenery gets drawn first to get layering right.
for(var i = 0; i < 15; i++) {
for (var j = Math.floor(vX / 16) - 1; j < Math.floor(vX / 16) + 20; j++){
if (level.scenery[i][j]) {
renderEntity(level.scenery[i][j]);
}
}
}
//then items
level.items.forEach (function (item) {
renderEntity(item);
});
level.enemies.forEach (function(enemy) {
renderEntity(enemy);
});
fireballs.forEach(function(fireball) {
renderEntity(fireball);
})
//then we draw every static object.
for(var i = 0; i < 15; i++) {
for (var j = Math.floor(vX / 16) - 1; j < Math.floor(vX / 16) + 20; j++){
if (level.statics[i][j]) {
renderEntity(level.statics[i][j]);
}
if (level.blocks[i][j]) {
renderEntity(level.blocks[i][j]);
updateables.push(level.blocks[i][j]);
}
}
}
//then the player
if (player.invincibility % 2 === 0) {
renderEntity(player);
}
//Mario goes INTO pipes, so naturally they go after.
level.pipes.forEach (function(pipe) {
renderEntity(pipe);
});
}
function renderEntity(entity) {
entity.render(ctx, vX, vY);
}

View File

@@ -0,0 +1,129 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//TODO: On console the hitbox is smaller. Measure it and edit this.
var Goomba = Mario.Goomba = function(pos, sprite) {
this.dying = false;
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [0,0,16,16]
});
this.vel[0] = -0.5;
this.idx = level.enemies.length;
};
Goomba.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
};
Goomba.prototype.update = function(dt, vX) {
if (this.pos[0] - vX > 336) { //if we're too far away, do nothing.
return;
} else if (this.pos[0] - vX < -32) {
delete level.enemies[this.idx];
}
if (this.dying) {
this.dying -= 1;
if (!this.dying) {
delete level.enemies[this.idx];
}
}
this.acc[1] = 0.2;
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
};
Goomba.prototype.collideWall = function() {
this.vel[0] = -this.vel[0];
};
Goomba.prototype.checkCollisions = function() {
if (this.flipping) {
return;
}
var h = this.pos[1] % 16 === 0 ? 1 : 2;
var w = this.pos[0] % 16 === 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.enemies[this.idx];
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
var that = this;
level.enemies.forEach(function(enemy){
if (enemy === that) { //don't check collisions with ourselves.
return;
} else if (enemy.pos[0] - vX > 336){ //stop checking once we get to far away dudes.
return;
} else {
that.isCollideWith(enemy);
}
});
this.isCollideWith(player);
};
Goomba.prototype.isCollideWith = function(ent) {
if (ent instanceof Mario.Player && (this.dying || ent.invincibility)) {
return;
}
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [ent.pos[0] + ent.hitbox[0], ent.pos[1] + ent.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
if (ent instanceof Mario.Player) { //if we hit the player
if (ent.vel[1] > 0) { //then the goomba dies
this.stomp();
} else if (ent.starTime) {
this.bump();
} else { //or the player gets hit
ent.damage();
}
} else {
this.collideWall();
}
}
}
};
Goomba.prototype.stomp = function() {
sounds.stomp.play();
player.bounce = true;
this.sprite.pos[0] = 32;
this.sprite.speed = 0;
this.vel[0] = 0;
this.dying = 10;
};
Goomba.prototype.bump = function() {
sounds.kick.play();
this.sprite.img = 'sprites/enemyr.png';
this.flipping = true;
this.pos[1] -= 1;
this.vel[0] = 0;
this.vel[1] = -2.5;
};
})();

View File

@@ -0,0 +1,54 @@
(function() {
var pressedKeys = {};
function setKey(event, status) {
var code = event.keyCode;
var key;
switch(code) {
case 32:
key = 'SPACE'; break;
case 37:
key = 'LEFT'; break;
case 38:
key = 'UP'; break;
case 39:
key = 'RIGHT'; break;
case 40:
key = 'DOWN'; break;
case 88:
key = 'JUMP'; break;
case 90:
key = 'RUN'; break;
default:
key = String.fromCharCode(code);
}
pressedKeys[key] = status;
}
document.addEventListener('keydown', function(e) {
setKey(e, true);
});
document.addEventListener('keyup', function(e) {
setKey(e, false);
});
window.addEventListener('blur', function() {
pressedKeys = {};
});
window.input = {
isDown: function(key) {
return pressedKeys[key.toUpperCase()];
},
reset: function() {
pressedKeys['RUN'] = false;
pressedKeys['LEFT'] = false;
pressedKeys['RIGHT'] = false;
pressedKeys['DOWN'] = false;
pressedKeys['JUMP'] = false;
}
};
})();

View File

@@ -0,0 +1,211 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Koopa = Mario.Koopa = function(pos, sprite, para) {
this.dying = false;
this.shell = false;
this.para = para; //para. As in, is it a paratroopa?
//So, funny story. The actual hitboxes don't reach all the way to the ground.
//What that means is, as long as I use them to keep things on the floor
//making the hitboxes accurate will make enemies sink into the ground.
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [2,8,12,24]
});
this.vel[0] = -0.5;
this.idx = level.enemies.length;
};
Koopa.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
};
Koopa.prototype.update = function(dt, vX) {
if (this.turn) {
this.vel[0] = -this.vel[0];
if (this.shell) sounds.bump.play();
this.turn = false;
}
if (this.vel[0] != 0) {
this.left = (this.vel[0] < 0);
}
if (this.left) {
this.sprite.img = 'sprites/enemy.png';
} else {
this.sprite.img = 'sprites/enemyr.png';
}
if (this.pos[0] - vX > 336) { //if we're too far away, do nothing.
return;
} else if (this.pos[0] - vX < -32) {
delete level.enemies[this.idx];
}
if (this.dying) {
this.dying -= 1;
if (!this.dying) {
delete level.enemies[this.idx];
}
}
if (this.shell) {
if (this.vel[0] == 0) {
this.shell -= 1;
if (this.shell < 120) {
this.sprite.speed = 5;
}
if (this.shell == 0) {
this.sprite = level.koopaSprite();
this.hitbox = [2,8,12,24]
if (this.left) {
this.sprite.img = 'sprites/enemyr.png';
this.vel[0] = 0.5;
this.left = false;
} else {
this.vel[0] = -0.5;
this.left = true;
}
this.pos[1] -= 16;
}
} else {
this.shell = 360;
this.sprite.speed = 0;
this.sprite.setFrame(0);
}
}
this.acc[1] = 0.2;
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
};
Koopa.prototype.collideWall = function() {
//This stops us from flipping twice on the same frame if we collide
//with multiple wall tiles simultaneously.
this.turn = true;
};
Koopa.prototype.checkCollisions = function() {
var h = this.shell ? 1 : 2;
if (this.pos[1] % 16 !== 0) {
h += 1;
}
var w = this.pos[0] % 16 === 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.enemies[this.idx];
return;
}
if (this.flipping) {
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
var that = this;
level.enemies.forEach(function(enemy){
if (enemy === that) { //don't check collisions with ourselves.
return;
} else if (enemy.pos[0] - vX > 336){ //stop checking once we get to far away dudes.
return;
} else {
that.isCollideWith(enemy);
}
});
this.isCollideWith(player);
};
Koopa.prototype.isCollideWith = function(ent) {
if (ent instanceof Mario.Player && (this.dying || ent.invincibility)) {
return;
}
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [ent.pos[0] + ent.hitbox[0], ent.pos[1] + ent.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
if (ent instanceof Mario.Player) {
if (ent.vel[1] > 0) {
player.bounce = true;
}
if (this.shell) {
sounds.kick.play();
if (this.vel[0] === 0) {
if (ent.left) { //I'm pretty sure this isn't the real logic.
this.vel[0] = -4;
} else {
this.vel[0] = 4;
}
} else {
if (ent.bounce) {
this.vel[0] = 0;
} else ent.damage();
}
} else if (ent.vel[1] > 0) { //then we get BOPPED.
this.stomp();
} else { //or the player gets hit
ent.damage();
}
} else {
if (this.shell && (ent instanceof Mario.Goomba)) {
ent.bump();
} else this.collideWall();
}
}
}
};
Koopa.prototype.stomp = function() {
//Turn this thing into a shell if it isn't already. Kick it if it is.
player.bounce = true;
if (this.para) {
this.para = false;
this.sprite.pos[0] -= 32;
} else {
sounds.stomp.play();
this.shell = 360;
this.sprite.pos[0] += 64;
this.sprite.pos[1] += 16;
this.sprite.size = [16,16];
this.hitbox = [2,0,12,16];
this.sprite.speed = 0;
this.frames = [0,1];
this.vel = [0,0];
this.pos[1] += 16;
}
};
Koopa.prototype.bump = function() {
sounds.kick.play();
if (this.flipping) return;
this.flipping = true;
this.sprite.pos = [160, 0];
this.sprite.size = [16,16];
this.hitbox = [2, 0, 12, 16];
this.sprite.speed = 0;
this.vel[0] = 0;
this.vel[1] = -2.5;
};
})();

View File

@@ -0,0 +1,219 @@
var oneone = Mario.oneone = function() {
//The things that need to be passed in are basically just dependent on what
//tileset we're in, so it makes more sense to just make one variable for that, so
//TODO: put as much of this in the Level object definition as possible.
level = new Mario.Level({
playerPos: [56,192],
loader: Mario.oneone,
background: "#7974FF",
scrolling: true,
invincibility: [144, 192, 240],
exit: 204,
floorSprite: new Mario.Sprite('sprites/tiles.png', [0,0],[16,16],0),
cloudSprite: new Mario.Sprite('sprites/tiles.png', [0,320],[48,32],0),
wallSprite: new Mario.Sprite('sprites/tiles.png', [0, 16],[16,16],0),
brickSprite: new Mario.Sprite('sprites/tiles.png', [16, 0], [16,16], 0),
brickBounceSprite: new Mario.Sprite('sprites/tiles.png',[32,0],[16,16],0),
rubbleSprite: function () {
return new Mario.Sprite('sprites/items.png', [64,0], [8,8], 3, [0,1])
},
ublockSprite: new Mario.Sprite('sprites/tiles.png', [48, 0], [16,16],0),
superShroomSprite: new Mario.Sprite('sprites/items.png', [0,0], [16,16], 0),
fireFlowerSprite: new Mario.Sprite('sprites/items.png', [0,32], [16,16], 20, [0,1,2,3]),
starSprite: new Mario.Sprite('sprites/items.png', [0,48], [16,16], 20, [0,1,2,3]),
pipeLEndSprite: new Mario.Sprite('sprites/tiles.png', [0, 128], [16,16], 0),
pipeREndSprite: new Mario.Sprite('sprites/tiles.png', [16, 128], [16,16], 0),
pipeLMidSprite: new Mario.Sprite('sprites/tiles.png', [0, 144], [16,16], 0),
pipeRMidSprite: new Mario.Sprite('sprites/tiles.png', [16, 144], [16,16], 0),
pipeUpMid: new Mario.Sprite('sprites/tiles.png', [0, 144], [32,16], 0),
pipeSideMid: new Mario.Sprite('sprites/tiles.png', [48, 128], [16,32], 0),
pipeLeft: new Mario.Sprite('sprites/tiles.png', [32, 128], [16,32], 0),
pipeTop: new Mario.Sprite('sprites/tiles.png', [0, 128], [32,16], 0),
qblockSprite: new Mario.Sprite('sprites/tiles.png', [384, 0], [16,16], 8, [0,0,0,0,1,2,1]),
bcoinSprite: function() {
return new Mario.Sprite('sprites/items.png', [0,112],[16,16], 20,[0,1,2,3]);
},
cloudSprites:[
new Mario.Sprite('sprites/tiles.png', [0,320],[16,32],0),
new Mario.Sprite('sprites/tiles.png', [16,320],[16,32],0),
new Mario.Sprite('sprites/tiles.png', [32,320],[16,32],0)
],
hillSprites: [
new Mario.Sprite('sprites/tiles.png', [128,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [144,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [160,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [128,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [144,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [160,144],[16,16],0)
],
bushSprite: new Mario.Sprite('sprites/tiles.png', [176, 144], [48, 16], 0),
bushSprites: [
new Mario.Sprite('sprites/tiles.png', [176,144], [16,16],0),
new Mario.Sprite('sprites/tiles.png', [192,144], [16,16],0),
new Mario.Sprite('sprites/tiles.png', [208,144], [16,16],0)],
goombaSprite: function() {
return new Mario.Sprite('sprites/enemy.png', [0, 16], [16,16], 3, [0,1]);
},
koopaSprite: function() {
return new Mario.Sprite('sprites/enemy.png', [96,0], [16,32], 2, [0,1]);
},
flagPoleSprites: [
new Mario.Sprite('sprites/tiles.png', [256, 128], [16,16], 0),
new Mario.Sprite('sprites/tiles.png', [256, 144], [16,16], 0),
new Mario.Sprite('sprites/items.png', [128, 32], [16,16], 0)
]
});
ground = [[0,69],[71,86],[89,153],[155,212]];
player.pos[0] = level.playerPos[0];
player.pos[1] = level.playerPos[1];
vX = 0;
//build THE GROUND
ground.forEach(function(loc) {
level.putFloor(loc[0],loc[1]);
});
//build scenery
clouds = [[7,3],[19, 2],[56, 3],[67, 2],[87, 2],[103, 2],[152, 3],[163, 2],[200, 3]];
clouds.forEach(function(cloud){
level.putCloud(cloud[0],cloud[1]);
});
twoClouds = [[36,2],[132,2],[180,2]];
twoClouds.forEach(function(cloud){
level.putTwoCloud(cloud[0],cloud[1]);
});
threeClouds = [[27,3],[75,3],[123,3],[171,3]];
threeClouds.forEach(function(cloud){
level.putThreeCloud(cloud[0],cloud[1]);
});
bHills = [0,48,96,144,192]
bHills.forEach(function(hill) {
level.putBigHill(hill, 12);
});
sHills = [16,64,111,160];
sHills.forEach(function(hill) {
level.putSmallHill(hill, 12);
});
bushes = [23,71,118,167];
bushes.forEach(function(bush) {
level.putBush(bush, 12);
});
twoBushes = [41,89,137];
twoBushes.forEach(function(bush) {
level.putTwoBush(bush, 12);
});
threeBushes = [11,59,106];
threeBushes.forEach(function(bush) {
level.putThreeBush(bush, 12);
});
//interactable terrain
level.putQBlock(16, 9, new Mario.Bcoin([256, 144]));
level.putBrick(20, 9, null);
level.putQBlock(21, 9, new Mario.Mushroom([336, 144]));
level.putBrick(22, 9, null);
level.putQBlock(22, 5, new Mario.Bcoin([352, 80]));
level.putQBlock(23, 9, new Mario.Bcoin([368, 144]));
level.putBrick(24, 9, null);
level.putPipe(28, 13, 2);
level.putPipe(38, 13, 3);
level.putPipe(46, 13, 4);
level.putRealPipe(57, 9, 4, "DOWN", Mario.oneonetunnel);
level.putBrick(77, 9, null);
level.putQBlock(78, 9, new Mario.Mushroom([1248, 144]));
level.putBrick(79, 9, null);
level.putBrick(80, 5, null);
level.putBrick(81, 5, null);
level.putBrick(82, 5, null);
level.putBrick(83, 5, null);
level.putBrick(84, 5, null);
level.putBrick(85, 5, null);
level.putBrick(86, 5, null);
level.putBrick(87, 5, null);
level.putBrick(91, 5, null);
level.putBrick(92, 5, null);
level.putBrick(93, 5, null);
level.putQBlock(94, 5, new Mario.Bcoin([1504, 80]));
level.putBrick(94, 9, null);
level.putBrick(100, 9, new Mario.Star([1600, 144]));
level.putBrick(101, 9, null);
level.putQBlock(105, 9, new Mario.Bcoin([1680, 144]));
level.putQBlock(108, 9, new Mario.Bcoin([1728, 144]));
level.putQBlock(108, 5, new Mario.Mushroom([1728, 80]));
level.putQBlock(111, 9, new Mario.Bcoin([1776, 144]));
level.putBrick(117, 9, null);
level.putBrick(120, 5, null);
level.putBrick(121, 5, null);
level.putBrick(122, 5, null);
level.putBrick(123, 5, null);
level.putBrick(128, 5, null);
level.putQBlock(129, 5, new Mario.Bcoin([2074, 80]));
level.putBrick(129, 9, null);
level.putQBlock(130, 5, new Mario.Bcoin([2080, 80]));
level.putBrick(130, 9, null);
level.putBrick(131, 5, null);
level.putWall(134, 13, 1);
level.putWall(135, 13, 2);
level.putWall(136, 13, 3);
level.putWall(137, 13, 4);
level.putWall(140, 13, 4);
level.putWall(141, 13, 3);
level.putWall(142, 13, 2);
level.putWall(143, 13, 1);
level.putWall(148, 13, 1);
level.putWall(149, 13, 2);
level.putWall(150, 13, 3);
level.putWall(151, 13, 4);
level.putWall(152, 13, 4);
level.putWall(155, 13, 4);
level.putWall(156, 13, 3);
level.putWall(157, 13, 2);
level.putWall(158, 13, 1);
level.putPipe(163, 13, 2);
level.putBrick(168, 9, null);
level.putBrick(169, 9, null);
level.putQBlock(170, 9, new Mario.Bcoin([2720, 144]));
level.putBrick(171, 9, null);
level.putPipe(179, 13, 2);
level.putWall(181, 13, 1);
level.putWall(182, 13, 2);
level.putWall(183, 13, 3);
level.putWall(184, 13, 4);
level.putWall(185, 13, 5);
level.putWall(186, 13, 6);
level.putWall(187, 13, 7);
level.putWall(188, 13, 8);
level.putWall(189, 13, 8);
level.putFlagpole(198);
//and enemies
level.putGoomba(22, 12);
level.putGoomba(40, 12);
level.putGoomba(50, 12);
level.putGoomba(51, 12);
level.putGoomba(82, 4);
level.putGoomba(84, 4);
level.putGoomba(100, 12);
level.putGoomba(102, 12);
level.putGoomba(114, 12);
level.putGoomba(115, 12);
level.putGoomba(122, 12);
level.putGoomba(123, 12);
level.putGoomba(125, 12);
level.putGoomba(126, 12);
level.putGoomba(170, 12);
level.putGoomba(172, 12);
level.putKoopa(35, 11);
music.underground.pause();
// music.overworld.currentTime = 0;
music.overworld.play();
};

View File

@@ -0,0 +1,65 @@
var oneonetunnel = Mario.oneonetunnel = function() {
level = new Mario.Level({
playerPos: [40,16],
loader: Mario.oneonetunnel,
background: "#000000",
scrolling: false,
coinSprite: function() {
return new Mario.Sprite('sprites/items.png', [0,96],[16,16], 6,[0,0,0,0,1,2,1]);
},
floorSprite: new Mario.Sprite('sprites/tiles.png', [0,32],[16,16],0),
wallSprite: new Mario.Sprite('sprites/tiles.png', [32, 32],[16,16],0),
brickSprite: new Mario.Sprite('sprites/tiles.png', [16, 0], [16,16], 0),
brickBounceSprite: new Mario.Sprite('sprites/tiles.png',[32,0],[16,16],0),
ublockSprite: new Mario.Sprite('sprites/tiles.png', [48, 0], [16,16],0),
pipeLMidSprite: new Mario.Sprite('sprites/tiles.png', [0, 144], [16,16], 0),
pipeRMidSprite: new Mario.Sprite('sprites/tiles.png', [16, 144], [16,16], 0),
pipeLEndSprite: new Mario.Sprite('sprites/tiles.png', [0, 128], [16,16], 0),
pipeREndSprite: new Mario.Sprite('sprites/tiles.png', [16, 128], [16,16], 0),
pipeUpMid: new Mario.Sprite('sprites/tiles.png', [0, 144], [32,16], 0),
pipeSideMid: new Mario.Sprite('sprites/tiles.png', [48, 128], [16,32], 0),
pipeLeft: new Mario.Sprite('sprites/tiles.png', [32, 128], [16,32], 0),
pipeTop: new Mario.Sprite('sprites/tiles.png', [0, 128], [32,16], 0),
LPipeSprites:[
new Mario.Sprite('sprites/tiles.png', [32,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [32,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [48,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [48,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [64,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [64,144],[16,16],0),
]
});
player.pos[0] = level.playerPos[0];
player.pos[1] = level.playerPos[1];
vX = 0;
level.putFloor(0,16);
level.putWall(0,13,11);
walls = [4,5,6,7,8,9,10];
walls.forEach(function(loc){
level.putWall(loc,13,3);
level.putWall(loc,3,1);
});
coins = [[5,5], [6,5], [7,5], [8,5], [9,5],
[4,7], [5,7], [6,7], [7,7], [8,7], [9,7], [10,7],
[4,9], [5,9], [6,9], [7,9], [8,9], [9,9], [10,9]];
coins.forEach(function(pos){
level.putCoin(pos[0],pos[1]);
});
//level.putLeftPipe(13,11);
level.putRealPipe(13,11,3,"RIGHT", function() {
Mario.oneone.call();
player.pos = [2616, 177]
player.pipe("UP", function() {;});
});
level.putPipe(15,13,13);
music.overworld.pause();
music.underground.currentTime = 0;
music.underground.play();
};

View File

@@ -0,0 +1,218 @@
(function() {
var Level = Mario.Level = function(options) {
this.playerPos = options.playerPos;
this.scrolling = options.scrolling;
this.loader = options.loader;
this.background = options.background;
this.exit = options.exit;
this.floorSprite = options.floorSprite;
this.cloudSprite = options.cloudSprite;
this.wallSprite = options.wallSprite;
this.brickSprite = options.brickSprite;
this.rubbleSprite = options.rubbleSprite;
this.brickBounceSprite = options.brickBounceSprite;
this.ublockSprite = options.ublockSprite;
this.superShroomSprite = options.superShroomSprite;
this.fireFlowerSprite = options.fireFlowerSprite;
this.starSprite = options.starSprite;
this.coinSprite = options.coinSprite;
this.bcoinSprite = options.bcoinSprite;
this.goombaSprite = options.goombaSprite;
this.koopaSprite = options.koopaSprite;
//prop pipe sprites, to be phased out
this.pipeLEndSprite = options.pipeLEndSprite;
this.pipeREndSprite = options.pipeREndSprite;
this.pipeLMidSprite = options.pipeLMidSprite;
this.pipeRMidSprite = options.pipeRMidSprite;
//real pipe sprites, use these.
this.pipeUpMid = options.pipeUpMid;
this.pipeSideMid = options.pipeSideMid;
this.pipeLeft = options.pipeLeft;
this.pipeTop = options.pipeTop;
this.flagpoleSprites = options.flagPoleSprites;
this.LPipeSprites = options.LPipeSprites;
this.cloudSprites = options.cloudSprites;
this.hillSprites = options.hillSprites;
this.bushSprite = options.bushSprite;
this.bushSprites = options.bushSprites;
this.qblockSprite = options.qblockSprite;
this.invincibility = options.invincibility;
this.statics = [];
this.scenery = [];
this.blocks = [];
this.enemies = [];
this.items = [];
this.pipes = [];
for (var i = 0; i < 15; i++) {
this.statics[i] = [];
this.scenery[i] = [];
this.blocks[i] = [];
}
};
Level.prototype.putFloor = function(start, end) {
for (var i = start; i < end; i++) {
this.statics[13][i] = new Mario.Floor([16*i,208], this.floorSprite);
this.statics[14][i] = new Mario.Floor([16*i,224], this.floorSprite);
}
};
Level.prototype.putGoomba = function(x, y) {
this.enemies.push(new Mario.Goomba([16*x, 16*y], this.goombaSprite() ));
};
Level.prototype.putKoopa = function(x, y) {
this.enemies.push(new Mario.Koopa([16*x, 16*y], this.koopaSprite(), false));
};
Level.prototype.putWall = function(x, y, height) {
//y is the bottom of the wall in this case.
for (var i = y-height; i < y; i++) {
this.statics[i][x] = new Mario.Floor([16*x, 16*i], this.wallSprite);
}
};
Level.prototype.putPipe = function(x, y, height) {
for (var i = y - height; i < y; i++) {
if (i === y - height) {
this.statics[i][x] = new Mario.Floor([16*x, 16*i], this.pipeLEndSprite);
this.statics[i][x+1] = new Mario.Floor([16*x+16, 16*i], this.pipeREndSprite);
} else {
this.statics[i][x] = new Mario.Floor([16*x, 16*i], this.pipeLMidSprite);
this.statics[i][x+1] = new Mario.Floor([16*x+16, 16*i], this.pipeRMidSprite);
}
}
};
//sometimes, pipes don't go straight up and down.
Level.prototype.putLeftPipe = function(x,y) {
this.statics[y][x] = new Mario.Floor([16*x, 16*y], this.LPipeSprites[0]);
this.statics[y+1][x] = new Mario.Floor([16*x,16*(y+1)], this.LPipeSprites[1]);
this.statics[y][x+1] = new Mario.Floor([16*(x+1),16*y], this.LPipeSprites[2]);
this.statics[y+1][x+1] = new Mario.Floor([16*(x+1),16*(y+1)], this.LPipeSprites[3]);
this.statics[y][x+2] = new Mario.Floor([16*(x+2),16*y], this.LPipeSprites[4]);
this.statics[y+1][x+2] = new Mario.Floor([16*(x+2),16*(y+1)], this.LPipeSprites[5]);
};
Level.prototype.putCoin = function(x, y) {
this.items.push(new Mario.Coin(
[x*16, y*16],
this.coinSprite()
));
};
Level.prototype.putCloud = function(x, y) {
this.scenery[y][x] = new Mario.Prop([x*16, y*16], this.cloudSprite);
};
Level.prototype.putQBlock = function(x, y, item) {
this.blocks[y][x] = new Mario.Block( {
pos: [x*16, y*16],
item: item,
sprite: this.qblockSprite,
usedSprite: this.ublockSprite
});
};
Level.prototype.putBrick = function(x,y,item) {
this.blocks[y][x] = new Mario.Block({
pos: [x*16, y*16],
item: item,
sprite: this.brickSprite,
bounceSprite: this.brickBounceSprite,
usedSprite: this.ublockSprite,
breakable: !item
});
};
Level.prototype.putBigHill = function(x, y) {
var px = x*16, py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.hillSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.hillSprites[3]);
this.scenery[y-1][x+1] = new Mario.Prop([px+16, py-16], this.hillSprites[0]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.hillSprites[4]);
this.scenery[y-1][x+2] = new Mario.Prop([px+32, py-16], this.hillSprites[3]);
this.scenery[y-2][x+2] = new Mario.Prop([px+32, py-32], this.hillSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.hillSprites[5]);
this.scenery[y-1][x+3] = new Mario.Prop([px+48, py-16], this.hillSprites[2]);
this.scenery[y][x+4] = new Mario.Prop([px+64, py], this.hillSprites[2]);
};
Level.prototype.putBush = function(x, y) {
this.scenery[y][x] = new Mario.Prop([x*16, y*16], this.bushSprite);
};
Level.prototype.putThreeBush = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.bushSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.bushSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.bushSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.bushSprites[1]);
this.scenery[y][x+4] = new Mario.Prop([px+64, py], this.bushSprites[2]);
};
Level.prototype.putTwoBush = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.bushSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.bushSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.bushSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.bushSprites[2]);
};
Level.prototype.putSmallHill = function(x, y) {
var px = x*16, py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.hillSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.hillSprites[3]);
this.scenery[y-1][x+1] = new Mario.Prop([px+16, py-16], this.hillSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.hillSprites[2]);
};
Level.prototype.putTwoCloud = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.cloudSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.cloudSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.cloudSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.cloudSprites[2]);
};
Level.prototype.putThreeCloud = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.cloudSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.cloudSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.cloudSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.cloudSprites[1]);
this.scenery[y][x+4] = new Mario.Prop([px+64, py], this.cloudSprites[2]);
};
Level.prototype.putRealPipe = function(x, y, length, direction, destination) {
px = x*16;
py = y*16;
this.pipes.push(new Mario.Pipe({
pos: [px, py],
length: length,
direction: direction,
destination: destination
}));
}
Level.prototype.putFlagpole = function(x) {
this.statics[12][x] = new Mario.Floor([16*x, 192], this.wallSprite);
for (i=3; i < 12; i++) {
this.scenery[i][x] = new Mario.Prop([16*x, 16*i], this.flagpoleSprites[1])
}
this.scenery[2][x] = new Mario.Prop([16*x, 32], this.flagpoleSprites[0]);
this.items.push(new Mario.Flag(16*x));
}
})();

View File

@@ -0,0 +1,118 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Mushroom = Mario.Mushroom = function(pos) {
this.spawning = false;
this.waiting = 0;
Mario.Entity.call(this, {
pos: pos,
sprite: level.superShroomSprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Mushroom, Mario.Entity);
Mushroom.prototype.render = function(ctx, vX, vY) {
if (this.spawning > 1) return;
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Mushroom.prototype.spawn = function() {
if (player.power > 0) {
//replace this with a fire flower
var ff = new Mario.Fireflower(this.pos)
ff.spawn();
return;
}
sounds.itemAppear.play();
this.idx = level.items.length;
level.items.push(this);
this.spawning = 12;
this.targetpos = [];
this.targetpos[0] = this.pos[0];
this.targetpos[1] = this.pos[1] - 16;
}
Mushroom.prototype.update = function(dt) {
if (this.spawning > 1) {
this.spawning -= 1;
if (this.spawning == 1) this.vel[1] = -.5;
return;
}
if (this.spawning) {
if (this.pos[1] <= this.targetpos[1]) {
this.pos[1] = this.targetpos[1];
this.vel[1] = 0;
this.waiting = 5;
this.spawning = 0;
this.vel[0] = 1;
}
} else {
this.acc[1] = 0.2;
}
if (this.waiting) {
this.waiting -= 1;
} else {
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
}
}
Mushroom.prototype.collideWall = function() {
this.vel[0] = -this.vel[0];
}
Mushroom.prototype.checkCollisions = function() {
if(this.spawning) {
return;
}
var h = this.pos[1] % 16 == 0 ? 1 : 2;
var w = this.pos[0] % 16 == 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.items[this.idx];
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
this.isPlayerCollided();
}
//we have access to player everywhere, so let's just do this.
Mushroom.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
player.powerUp(this.idx);
}
}
}
Mushroom.prototype.bump = function() {
this.vel[1] = -2;
}
})();

View File

@@ -0,0 +1,45 @@
Enemy:
#collideWith
checks collision. Bounces off of walls and other enemies. Kills marios.
gets killed by block bonk from below
Specific enemy behaviors:
koopas get replaced with shells
shells get kicked if touched from the side, instead of killing
Mario:
#collideWith
gets expelled from walls, bonks blocks, destroys blocks as big Mario
note: collision with blocks is determined by which one is over the center
if you're slightly to the side, you slip past it.
Crazy-ass sprite shifting if you're in star mode!
Item
#collideWith
item pickup logic
Mushroom
#update
movement logic for mushrooms
get impulse from being block bonked
this can be implemented by giving them the appropriate accel
just if they overlap a block from the bottom.
of course, only AFTER they finish sliding out of the block.
Water levels:
make a separate class for water Marios
less gravity
swimming sprite
fireballs are the same
jump works when not on the ground
different enemies
TODO: Make level loader use hashes instead of arrays where possible.
Should be a free performance gain.
Should also make item deletion less weird.
TODO: Make gravity exist higher up instead of having a magic number.
Note from the future: Nope. Different objects have different gravity.

View File

@@ -0,0 +1,160 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//there are too many possible configurations of pipe to capture in a reasonable
//set of simple variables. Joints, etc. are just too much.
//To that end, the pipe class handles simple pipes, and we'll put together
//anything more complex with individual props. OK? OK.
Pipe = Mario.Pipe = function(options) {
this.pos = options.pos
//NOTE: direction is the direction you move INTO the pipe.
this.direction = options.direction
this.destination = options.destination
this.length = options.length;
if (this.direction === "UP" || this.direction === "DOWN") {
this.hitbox = [0,0, 32, this.length * 16];
this.midsection = level.pipeUpMid;
this.endsection = level.pipeTop;
} else {
this.hitbox = [0,0, 16*this.length, 32];
this.midsection = level.pipeSideMid;
this.endsection = level.pipeLeft;
}
}
Pipe.prototype.checkPipe = function() {
if (this.destination === undefined || !input.isDown(this.direction)) return;
var h = player.power===0 ? 16 : 32;
var x = Math.floor(player.pos[0]);
var y = Math.floor(player.pos[1]);
switch (this.direction) {
case 'RIGHT': if (x === this.pos[0]-16 &&
y >= this.pos[1] &&
y+h <= this.pos[1]+32) {
player.pipe(this.direction, this.destination)
}
break;
case 'LEFT': if (x === this.pos[0]+16*this.length &&
y >= this.pos[1] &&
y+h <= this.pos[1]+32) {
player.pipe(this.direction, this.destination)
}
break;
case 'UP': if (y === this.pos[1] + 16*this.length &&
x >= this.pos[0] &&
x+16 <= this.pos[0]+32) {
player.pipe(this.direction, this.destination)
}
break;
case 'DOWN': if (y+h === this.pos[1] &&
x >= this.pos[0] &&
x+16 <= this.pos[0]+32) {
player.pipe(this.direction, this.destination);
}
break;
}
}
//Note to self: next time, decide on a convention for which thing checks for collisions
//and stick to it. This is a pain.
Pipe.prototype.checkCollisions = function() {
var that = this;
level.enemies.forEach (function(ent) {
that.isCollideWith(ent);
});
level.items.forEach (function(ent) {
that.isCollideWith(ent);
});
fireballs.forEach(function(ent){
that.isCollideWith(ent)
});
if (!player.piping) this.isCollideWith(player);
}
Pipe.prototype.isCollideWith = function (ent) {
//long story short: because we scan every item, and and one 'rubble' item is four things with separate positions
//we'll crash without this line as soon as we destroy a block. OOPS.
if (ent.pos === undefined) return;
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [Math.floor(this.pos[0] + this.hitbox[0]), Math.floor(this.pos[1] + this.hitbox[1])];
var hpos2 = [Math.floor(ent.pos[0] + ent.hitbox[0]), Math.floor(ent.pos[1] + ent.hitbox[1])];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
//if the entity is over the block, it's basically floor
var center = hpos2[0] + ent.hitbox[2] / 2;
if (Math.abs(hpos2[1] + ent.hitbox[3] - hpos1[1]) <= ent.vel[1]) {
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] - ent.hitbox[3] - ent.hitbox[1];
ent.standing = true;
if (ent instanceof Mario.Player) {
ent.jumping = 0;
}
} else if (Math.abs(hpos2[1] - hpos1[1] - this.hitbox[3]) > ent.vel[1] &&
center + 2 >= hpos1[0] && center - 2 <= hpos1[0] + this.hitbox[2]) {
//ent is under the block.
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] + this.hitbox[3];
if (ent instanceof Mario.Player) {
ent.jumping = 0;
}
} else {
//entity is hitting it from the side, we're a wall
ent.collideWall(this);
}
}
}
}
//we COULD try to write some shenanigans so that the check gets put into the
//collision code, but there won't ever be more than a handful of pipes in a level
//so the performance hit of scanning all of them is miniscule.
Pipe.prototype.update = function(dt) {
if (this.destination) this.checkPipe();
}
//http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array
//I honestly have no idea if javascript does this, but I feel like it makes sense
//stylistically to prefer branching outside of loops when possible as convention
//TODO: edit the spritesheet so UP and LEFT pipes aren't backwards.
Pipe.prototype.render = function(ctx, vX, vY) {
switch (this.direction) {
case "DOWN":
this.endsection.render(ctx, this.pos[0], this.pos[1], vX, vY);
for (var i = 1; i < this.length; i++) {
this.midsection.render(ctx, this.pos[0], this.pos[1]+i*16, vX, vY)
}
break;
case "UP":
this.endsection.render(ctx, this.pos[0], this.pos[1]+16*(this.length-1), vX, vY)
for (var i=0; i < this.length - 1; i++) {
this.midsection.render(ctx, this.pos[0], this.pos[1]+i*16, vX, vY)
}
break;
case "RIGHT":
this.endsection.render(ctx, this.pos[0], this.pos[1], vX, vY)
for (var i = 1; i < this.length; i++) {
this.midsection.render(ctx, this.pos[0]+16*i, this.pos[1], vX, vY)
}
break;
case "LEFT":
this.endsection.render(ctx, this.pos[0]+16*(this.length-1), this.pos[1], vX, vY)
for (var i = 0; i < this.legth-1; i++) {
this.midsection.render(ctx, this.pos[0], this.pos[1]+i*16, vX, vY)
}
break;
}
}
})();

View File

@@ -0,0 +1,446 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Player = Mario.Player = function(pos) {
//I know, I know, there are a lot of variables tracking Mario's state.
//Maybe these can be consolidated some way? We'll see once they're all in.
this.power = 0;
this.coins = 0;
this.powering = [];
this.bounce = false;
this.jumping = 0;
this.canJump = true;
this.invincibility = 0;
this.crouching = false;
this.fireballs = 0;
this.runheld = false;
this.noInput = false;
this.targetPos = [];
Mario.Entity.call(this, {
pos: pos,
sprite: new Mario.Sprite('sprites/player.png', [80,32],[16,16],0),
hitbox: [0,0,16,16]
});
};
Mario.Util.inherits(Player, Mario.Entity);
Player.prototype.run = function() {
this.maxSpeed = 2.5;
if (this.power == 2 && !this.runheld) {
this.shoot();
}
this.runheld = true;
}
Player.prototype.shoot = function() {
if (this.fireballs >= 2) return; //Projectile limit!
this.fireballs += 1;
var fb = new Mario.Fireball([this.pos[0]+8,this.pos[1]]); //I hate you, Javascript.
fb.spawn(this.left);
this.shooting = 2;
}
Player.prototype.noRun = function() {
this.maxSpeed = 1.5;
this.moveAcc = 0.07;
this.runheld = false;
}
Player.prototype.moveRight = function() {
//we're on the ground
if (this.vel[1] === 0 && this.standing) {
if (this.crouching) {
this.noWalk();
return;
}
this.acc[0] = this.moveAcc;
this.left = false;
} else {
this.acc[0] = this.moveAcc;
}
};
Player.prototype.moveLeft = function() {
if (this.vel[1] === 0 && this.standing) {
if (this.crouching) {
this.noWalk();
return;
}
this.acc[0] = -this.moveAcc;
this.left = true;
} else {
this.acc[0] = -this.moveAcc;
}
};
Player.prototype.noWalk = function() {
this.maxSpeed = 0;
if (this.vel[0] === 0) return;
if (Math.abs(this.vel[0]) <= 0.1) {
this.vel[0] = 0;
this.acc[0] = 0;
}
};
Player.prototype.crouch = function() {
if (this.power === 0) {
this.crouching = false;
return;
}
if (this.standing) this.crouching = true;
}
Player.prototype.noCrouch = function() {
this.crouching = false;
}
Player.prototype.jump = function() {
if (this.vel[1] > 0) {
return;
}
if (this.jumping) {
this.jumping -= 1;
} else if (this.standing && this.canJump) {
this.jumping = 20;
this.canJump = false;
this.standing = false;
this.vel[1] = -6;
if (this.power === 0) {
sounds.smallJump.currentTime = 0;
sounds.smallJump.play();
} else {
sounds.bigJump.currentTime = 0;
sounds.bigJump.play();
}
}
};
Player.prototype.noJump = function() {
this.canJump = true;
if (this.jumping) {
if (this.jumping <= 16) {
this.vel[1] = 0;
this.jumping = 0;
} else this.jumping -= 1;
}
};
Player.prototype.setAnimation = function() {
if (this.dying) return;
if (this.starTime) {
var index;
if (this.starTime > 60)
index = Math.floor(this.starTime / 2) % 3;
else index = Math.floor(this.starTime / 8) % 3;
this.sprite.pos[1] = level.invincibility[index];
if (this.power == 0) {
this.sprite.pos[1] += 32;
}
this.starTime -= 1;
if (this.starTime == 0) {
switch(this.power) {
case 0: this.sprite.pos[1] = 32; break;
case 1: this.sprite.pos[1] = 0; break;
case 2: this.sprite.pos[1] = 96; break;
}
}
}
//okay cool, now set the sprite
if (this.crouching) {
this.sprite.pos[0] = 176;
this.sprite.speed = 0;
return;
}
if (this.jumping) {
this.sprite.pos[0] = 160;
this.sprite.speed = 0;
} else if (this.standing) {
if (Math.abs(this.vel[0]) > 0) {
if (this.vel[0] * this.acc[0] >= 0) {
this.sprite.pos[0] = 96;
this.sprite.frames = [0,1,2];
if (this.vel[0] < 0.2) {
this.sprite.speed = 5;
} else {
this.sprite.speed = Math.abs(this.vel[0]) * 8;
}
} else if ((this.vel[0] > 0 && this.left) || (this.vel[0] < 0 && !this.left)){
this.sprite.pos[0] = 144;
this.sprite.speed = 0;
}
} else {
this.sprite.pos[0] = 80;
this.sprite.speed = 0;
}
if (this.shooting) {
this.sprite.pos[0] += 160;
this.shooting -= 1;
}
}
if (this.flagging) {
this.sprite.pos[0] = 192;
this.sprite.frames = [0,1];
this.sprite.speed = 10;
if (this.vel[1] === 0) this.sprite.frames = [0];
}
//which way are we facing?
if (this.left) {
this.sprite.img = 'sprites/playerl.png';
} else {
this.sprite.img = 'sprites/player.png';
}
};
Player.prototype.update = function(dt, vX) {
if (this.powering.length !== 0) {
var next = this.powering.shift();
if (next == 5) return;
this.sprite.pos = this.powerSprites[next];
this.sprite.size = this.powerSizes[next];
this.pos[1] += this.shift[next];
if (this.powering.length === 0) {
delete level.items[this.touchedItem];
}
return;
}
if (this.invincibility) {
this.invincibility -= Math.round(dt * 60);
}
if (this.waiting) {
this.waiting -= dt;
if (this.waiting <= 0) {
this.waiting = 0;
} else return;
}
if (this.bounce) {
this.bounce = false;
this.standing = false;
this.vel[1] = -3;
}
if (this.pos[0] <= vX) {
this.pos[0] = vX;
this.vel[0] = Math.max(this.vel[0], 0);
}
if (Math.abs(this.vel[0]) > this.maxSpeed) {
this.vel[0] -= 0.05 * this.vel[0] / Math.abs(this.vel[0]);
this.acc[0] = 0;
}
if (this.dying){
if (this.pos[1] < this.targetPos[1]) {
this.vel[1] = 1;
}
this.dying -= 1 * dt;
if (this.dying <= 0) {
player = new Mario.Player(level.playerPos);
level.loader.call();
input.reset();
}
}
else {
this.acc[1] = 0.25
if (this.pos[1] > 240) {
this.die();
}
}
if (this.piping) {
this.acc = [0,0];
var pos = [Math.round(this.pos[0]), Math.round(this.pos[1])]
if (pos[0] === this.targetPos[0] && pos[1] === this.targetPos[1]) {
this.piping = false;
this.pipeLoc.call();
}
}
if (this.flagging) {
this.acc = [0,0];
}
if (this.exiting) {
this.left = false;
this.flagging = false;
this.vel[0] = 1.5;
if (this.pos[0] >= this.targetPos[0]) {
this.sprite.size = [0,0];
this.vel = [0,0];
window.setTimeout(function() {
player.sprite.size = player.power===0 ? [16,16] : [16,32];
player.exiting = false;
player.noInput = false;
level.loader();
if (player.power !== 0) player.pos[1] -= 16;
music.overworld.currentTime = 0;
}, 5000);
}
}
//approximate acceleration
this.vel[0] += this.acc[0];
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.setAnimation();
this.sprite.update(dt);
};
Player.prototype.checkCollisions = function() {
if (this.piping || this.dying) return;
//x-axis first!
var h = this.power > 0 ? 2 : 1;
var w = 1;
if (this.pos[1] % 16 !== 0) {
h += 1;
}
if (this.pos[0] % 16 !== 0) {
w += 1;
}
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
for (var i = 0; i < h; i++) {
if (baseY + i < 0 || baseY + i >= 15) continue;
for (var j = 0; j < w; j++) {
if (baseY < 0) { i++;}
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
};
Player.prototype.powerUp = function(idx) {
sounds.powerup.play();
this.powering = [0,5,2,5,1,5,2,5,1,5,2,5,3,5,1,5,2,5,3,5,1,5,4];
this.touchedItem = idx;
if (this.power === 0) {
this.sprite.pos[0] = 80;
var newy = this.sprite.pos[1] - 32;
this.powerSprites = [[80, newy+32], [80, newy+32], [320, newy], [80, newy], [128, newy]];
this.powerSizes = [[16,16],[16,16],[16,32],[16,32],[16,32]];
this.shift = [0,16,-16,0,-16];
this.power = 1;
this.hitbox = [0,0,16,32];
} else if (this.power == 1) {
var curx = this.sprite.pos[0];
this.powerSprites = [[curx, 96], [curx, level.invincibility[0]],
[curx, level.invincibility[1]], [curx, level.invincibility[2]],
[curx, 96]];
this.powerSizes[[16,32],[16,32],[16,32],[16,32],[16,32]];
this.shift = [0,0,0,0,0];
this.power = 2;
} else {
this.powering = [];
delete level.items[idx];
//no animation, but we play the sound and you get 5000 points.
}
};
Player.prototype.damage = function() {
if (this.power === 0) { //if you're already small, you dead!
this.die();
} else { //otherwise, you get turned into small mario
sounds.pipe.play();
this.powering = [0,5,1,5,2,5,1,5,2,5,1,5,2,5,1,5,2,5,1,5,2,5,3];
this.shift = [0,16,-16,16];
this.sprite.pos = [160, 0];
this.powerSprites = [[160,0], [240, 32], [240, 0], [160, 32]];
this.powerSizes = [[16, 32], [16,16], [16,32], [16,16]];
this.invincibility = 120;
this.power = 0;
this.hitbox = [0,0,16,16];
}
};
Player.prototype.die = function () {
//TODO: rewrite the way sounds work to emulate the channels of an NES.
music.overworld.pause();
music.underground.pause();
music.overworld.currentTime = 0;
music.death.play();
this.noWalk();
this.noRun();
this.noJump();
this.acc[0] = 0;
this.sprite.pos = [176, 32];
this.sprite.speed = 0;
this.power = 0;
this.waiting = 0.5;
this.dying = 2;
if (this.pos[1] < 240) { //falling into a pit doesn't do the animation.
this.targetPos = [this.pos[0], this.pos[1]-128];
this.vel = [0,-5];
} else {
this.vel = [0,0];
this.targetPos = [this.pos[0], this.pos[1] - 16];
}
};
Player.prototype.star = function(idx) {
delete level.items[idx];
this.starTime = 660;
}
Player.prototype.pipe = function(direction, destination) {
sounds.pipe.play();
this.piping = true;
this.pipeLoc = destination;
switch(direction) {
case "LEFT":
this.vel = [-1,0];
this.targetPos = [Math.round(this.pos[0]-16), Math.round(this.pos[1])]
break;
case "RIGHT":
this.vel = [1,0];
this.targetPos = [Math.round(this.pos[0]+16), Math.round(this.pos[1])]
break;
case "DOWN":
this.vel = [0,1];
this.targetPos = [Math.round(this.pos[0]), Math.round(this.pos[1]+this.hitbox[3])]
break;
case "UP":
this.vel = [0,-1];
this.targetPos = [Math.round(this.pos[0]), Math.round(this.pos[1]-this.hitbox[3])]
break;
}
}
Player.prototype.flag = function() {
this.noInput = true;
this.flagging = true;
this.vel = [0, 2];
this.acc = [0, 0];
}
Player.prototype.exit = function() {
this.pos[0] += 16;
this.targetPos[0] = level.exit * 16;
this.left = true;
this.setAnimation();
this.waiting = 1;
this.exiting = true;
}
})();

View File

@@ -0,0 +1,15 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//props do even less than entities, so they don't need to inherit really
var Prop = Mario.Prop = function(pos, sprite) {
this.pos = pos;
this.sprite = sprite;
}
//but we will be using the same Render, more or less.
Prop.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
})();

View File

@@ -0,0 +1,62 @@
//simple resource loader
(function() {
var resourceCache = {};
var loading = [];
var readyCallbacks = [];
// Load an image url or an array of image urls
function load(urlOrArr) {
if(urlOrArr instanceof Array) {
urlOrArr.forEach(function(url) {
_load(url);
});
}
else {
_load(urlOrArr);
}
}
function _load(url) {
if(resourceCache[url]) {
return resourceCache[url];
}
else {
var img = new Image();
img.onload = function() {
resourceCache[url] = img;
if(isReady()) {
readyCallbacks.forEach(function(func) { func(); });
}
};
resourceCache[url] = false;
img.src = url;
}
}
function get(url) {
return resourceCache[url];
}
function isReady() {
var ready = true;
for(var k in resourceCache) {
if(resourceCache.hasOwnProperty(k) &&
!resourceCache[k]) {
ready = false;
}
}
return ready;
}
function onReady(func) {
readyCallbacks.push(func);
}
window.resources = {
load: load,
get: get,
onReady: onReady,
isReady: isReady
};
})();

View File

@@ -0,0 +1,55 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//TODO: make each rubble an entity, use that render and write in Entity.update
var Rubble = Mario.Rubble = function() {
this.sprites = [];
this.poss = [];
this.vels = [];
}
Rubble.prototype.spawn = function(pos) {
this.idx = level.items.length;
level.items.push(this);
this.sprites[0] = level.rubbleSprite();
this.sprites[1] = level.rubbleSprite();
this.sprites[2] = level.rubbleSprite();
this.sprites[3] = level.rubbleSprite();
this.poss[0] = pos;
this.poss[1] = [ pos[0] + 8, pos[1] ];
this.poss[2] = [ pos[0], pos[1] + 8 ];
this.poss[3] = [ pos[0] + 8, pos[1] + 8 ];
this.vels[0] = [-1.25, -5];
this.vels[1] = [1.25, -5];
this.vels[2] = [-1.25, -3];
this.vels[3] = [1.25, -3];
}
Rubble.prototype.update = function(dt) {
for(var i = 0; i < 4; i++) {
if (this.sprites[i]===undefined) continue;
this.vels[i][1] += .3;
this.poss[i][0] += this.vels[i][0];
this.poss[i][1] += this.vels[i][1];
this.sprites[i].update(dt);
if (this.poss[i][1] > 256) {
delete this.sprites[i];
}
}
if (this.sprites.every(function (el) {return !el})) {
delete level.items[this.idx];
}
}
//You might argue that things that can't collide are more like scenery
//but these move and need to be deleted, and i'd rather deal with the 1d array.
Rubble.prototype.checkCollisions = function() {;}
Rubble.prototype.render = function() {
for(var i = 0; i < 4; i++) {
if (this.sprites[i] === undefined) continue;
this.sprites[i].render(ctx, this.poss[i][0], this.poss[i][1], vX, vY);
}
}
})();

View File

@@ -0,0 +1,47 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Sprite = Mario.Sprite = function(img, pos, size, speed, frames, once) {
this.pos = pos;
this.size = size;
this.speed = speed;
this._index = 0;
this.img = img;
this.once = once;
this.frames = frames;
}
Sprite.prototype.update = function(dt, gameTime) {
if (gameTime && gameTime == this.lastUpdated) return;
this._index += this.speed*dt;
if (gameTime) this.lastUpdated = gameTime;
}
Sprite.prototype.setFrame = function(frame) {
this._index = frame;
}
Sprite.prototype.render = function(ctx, posx, posy, vX, vY) {
var frame;
if (this.speed > 0) {
var max = this.frames.length;
var idx = Math.floor(this._index);
frame = this.frames[idx % max];
if (this.once && idx >= max) {
this.done = true;
return;
}
} else {
frame = 0;
}
var x = this.pos[0];
var y = this.pos[1];
x += frame*this.size[0];
ctx.drawImage(resources.get(this.img), x + (1/3),y + (1/3), this.size[0] - (2/3), this.size[1] - (2/3), Math.round(posx - vX), Math.round(posy - vY), this.size[0],this.size[1]);
}
})();

View File

@@ -0,0 +1,116 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Star = Mario.Star = function(pos) {
this.spawning = false;
this.waiting = 0;
Mario.Entity.call(this, {
pos: pos,
sprite: level.starSprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Star, Mario.Entity);
Star.prototype.render = function(ctx, vX, vY) {
if (this.spawning > 1) return;
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Star.prototype.spawn = function() {
this.idx = level.items.length;
level.items.push(this);
this.spawning = 12;
this.targetpos = [];
this.targetpos[0] = this.pos[0];
this.targetpos[1] = this.pos[1] - 16;
}
Star.prototype.update = function(dt) {
if (this.spawning > 1) {
this.spawning -= 1;
if (this.spawning == 1) this.vel[1] = -.5;
return;
}
if (this.spawning) {
if (this.pos[1] <= this.targetpos[1]) {
this.pos[1] = this.targetpos[1];
this.vel[1] = 0;
this.waiting = 5;
this.spawning = 0;
this.vel[0] = 1;
}
} else {
this.acc[1] = 0.2;
}
if (this.standing) {
this.standing = false;
this.vel[1] = -3;
}
if (this.waiting) {
this.waiting -= 1;
} else {
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
}
}
Star.prototype.collideWall = function() {
this.vel[0] = -this.vel[0];
}
Star.prototype.checkCollisions = function() {
if(this.spawning) {
return;
}
var h = this.pos[1] % 16 == 0 ? 1 : 2;
var w = this.pos[0] % 16 == 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.items[this.idx];
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
this.isPlayerCollided();
}
//we have access to player everywhere, so let's just do this.
Star.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
player.star(this.idx);
}
}
}
Star.prototype.bump = function() {
this.vel[1] = -2;
}
})();

View File

@@ -0,0 +1,14 @@
(function() {
if (typeof Mario === 'undefined') {
window.Mario = {};
}
var Util = Mario.Util = {};
Util.inherits = function(subclass, superclass) {
function Surrogate() {};
Surrogate.prototype = superclass.prototype;
subclass.prototype = new Surrogate();
}
})()

View File

@@ -0,0 +1,144 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="../favicon.png" />
<link rel="stylesheet" href="../../build/pyscript.css" />
<script defer src="../../build/pyscript.js"></script>
</head>
<body>
<script type="text/javascript" src="js/util.js"></script>
<script type="text/javascript" src="js/input.js"></script>
<script type="text/javascript" src="js/resources.js"></script>
<script type="text/javascript" src="js/sprite.js"></script>
<script type="text/javascript" src="js/entity.js"></script>
<script type="text/javascript" src="js/pipe.js"></script>
<script type="text/javascript" src="js/mushroom.js"></script>
<script type="text/javascript" src="js/fireflower.js"></script>
<script type="text/javascript" src="js/star.js"></script>
<script type="text/javascript" src="js/fireball.js"></script>
<script type="text/javascript" src="js/coin.js"></script>
<script type="text/javascript" src="js/bcoin.js"></script>
<script type="text/javascript" src="js/goomba.js"></script>
<script type="text/javascript" src="js/koopa.js"></script>
<script type="text/javascript" src="js/floor.js"></script>
<script type="text/javascript" src="js/block.js"></script>
<script type="text/javascript" src="js/rubble.js"></script>
<script type="text/javascript" src="js/prop.js"></script>
<script type="text/javascript" src="js/player.js"></script>
<script type="text/javascript" src="js/flag.js"></script>
<script type="text/javascript" src="js/levels/level.js"></script>
<script type="text/javascript" src="js/levels/11.js"></script>
<script type="text/javascript" src="js/levels/11tunnel.js"></script>
<script type="text/javascript" src="js/game.js"></script>
<py-script>
from js import handTrack, setTimeout, requestAnimationFrame, player
from pyodide import create_once_callable
import asyncio
context = canvas.element.getContext("2d")
isVideo = False
model = None
last_position = 0
direction = "stop"
modelParams = {
"flipHorizontal": True, # flip e.g for video
"maxNumBoxes": 20, # maximum number of boxes to detect
"iouThreshold": 0.5, # ioU threshold for non-max suppression
"scoreThreshold": 0.6, # confidence threshold for predictions.
}
def toggle_video(evt):
global isVideo
player.jump()
if (not isVideo):
update_note.write("Starting video")
pyscript.run_until_complete(start_video())
else:
update_note.write("Stopping video")
handTrack.stopVideo(video.element)
isVideo = False
update_note.write("Video stopped")
async def start_video():
global isVideo
update_note.write("Inside sstart video")
status = await handTrack.startVideo(video.element)
console.log("video started", status)
if status:
update_note.write("Video started. Now tracking")
isVideo = True
console.log( "Calling RUN DETECTION")
y = await run_detection()
else:
update_note.write( "Please enable video")
def sync_run_detection(evt):
pyscript.run_until_complete(run_detection())
async def run_detection():
global model
global isVideo
global last_position
global direction
predictions = await model.detect(video.element)
model.renderPredictions(predictions, canvas.element, context, video.element);
if predictions:
curr_position = predictions[0].bbox[0] + (predictions[0].bbox[2] / 2)
delta = last_position - curr_position
last_position = curr_position
#console.log(delta, curr_position, last_position)
if abs(delta) < 2:
direction = "stop"
elif delta > 0:
direction = "left"
else:
direction = "right"
for prediction in predictions:
if prediction.label == 'open':
player.jump()
elif prediction.label == 'close':
player.crouch()
if (isVideo):
await requestAnimationFrame(create_once_callable(sync_run_detection));
def handle_model(lmodel):
global model
model = lmodel
update_note.write("Loaded Model!")
async def start():
model = await handTrack.load(modelParams)#.then(handle_model)
handle_model(model)
pyscript.run_until_complete(start())
</py-script>
<div class="mb10">
<p>Use < > to move, ↓ to crouch and x to jump. If video is enabled, say hi to jump as well! </p>
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" pys-onClick="toggle_video">
Start Video
</button>
<div id="update-note" py-mount class="updatenote mt10">loading model ..</div>
</div>
<div>
<video autoplay="autoplay" id="myvideo" py-mount="video"></video>
<canvas id="canvas" py-mount class="border canvasbox"></canvas>
</div>
<script src="../handtrack/lib/handtrack.min.js"> </script>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,37 @@
<html>
<head>
<title>Panel Example</title>
<meta charset="iso-8859-1">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.13.0-rc.10/dist/panel.min.js"></script>
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<py-env>
- bokeh
- numpy
</py-env>
<h1>Panel Example</h1>
<div id="myplot"></div>
<py-script>
import asyncio
import micropip
await micropip.install(['panel==0.13.0rc10'])
import panel as pn
slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')
def callback(new):
return f'Amplitude is: {new}'
row = pn.Row(slider, pn.bind(callback, slider))
await pn.io.pyodide.show(row, 'myplot')
</py-script>
</body>
</html>

View File

@@ -0,0 +1,180 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pyscript/Panel KMeans Demo</title>
<link rel="icon" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/icons/favicon.ico" type="">
<meta name="name" content="PyScript/Panel KMeans Demo">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/widgets.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/markdown.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/loading.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/dataframe.css" type="text/css" />
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@4.9.3/dist/js/tabulator.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/panel.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/bundled/bootstraptemplate/bootstrap.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/bundled/defaulttheme/default.css">
<style>
#sidebar {
width: 350px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<py-env>
- bokeh
- numpy
- pandas
- scikit-learn
</py-env>
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" style="" id="header">
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="app-header">
<a class="title" href="/" >&nbsp;Panel</a>
<span class="title">&nbsp;-</span>
<a class="title" href="" >&nbsp;Pyscript KMeans Clustering Demo</a>
</div>
</nav>
<div class="row overflow-hidden" id="content">
<div class="sidenav" id="sidebar">
<ul class="nav flex-column">
<div class="bk-root" id="x-widget" data-root-id="1021"></div>
<div class="bk-root" id="y-widget" data-root-id="1026"></div>
<div class="bk-root" id="n-widget" data-root-id="1031"></div>
</ul>
</div>
<div class="col mh-100 float-left" id="main">
<div class="bk-root" id="intro" data-root-id="1008"></div>
<div class="bk-root" id="cluster-plot" data-root-id="1009"></div>
<div class="bk-root" id="table" data-root-id="1009"></div>
</div>
</div>
</div>
<py-script>
import asyncio
import micropip
from io import StringIO
from js import fetch
await micropip.install(['panel==0.13.0rc10', 'altair'])
import altair as alt
import panel as pn
import pandas as pd
from panel.io.pyodide import show
from sklearn.cluster import KMeans
pn.config.sizing_mode = 'stretch_width'
data = await fetch('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-28/penguins.csv')
penguins = pd.read_csv(StringIO(await data.text())).dropna()
cols = list(penguins.columns)[2:6]
x = pn.widgets.Select(name='x', options=cols, value='bill_depth_mm')
y = pn.widgets.Select(name='y', options=cols, value='bill_length_mm')
n_clusters = pn.widgets.IntSlider(name='n_clusters', start=1, end=5, value=3)
brush = alt.selection_interval(name='brush') # selection of type "interval"
def get_clusters(n_clusters):
kmeans = KMeans(n_clusters=n_clusters)
est = kmeans.fit(penguins[cols].values)
df = penguins.copy()
df['labels'] = est.labels_.astype('str')
return df
def get_chart(x, y, df):
centers = df.groupby('labels').mean()
return (alt.Chart(df)
.mark_point(size=100)
.encode(
x=alt.X(x, scale=alt.Scale(zero=False)),
y=alt.Y(y, scale=alt.Scale(zero=False)),
shape='labels',
color='species'
).add_selection(brush).properties(width=800) +
alt.Chart(centers)
.mark_point(size=250, shape='cross', color='black')
.encode(x=x+':Q', y=y+':Q')
)
chart = pn.pane.Vega()
table = pn.widgets.Tabulator(pagination='remote', page_size=10)
def update_table(event=None):
table.value = get_clusters(n_clusters.value)
n_clusters.param.watch(update_table, 'value')
@pn.depends(x, y, n_clusters, watch=True)
def update_chart(*events):
chart.object = get_chart(x.value, y.value, table.value)
chart.selection.param.watch(update_filters, 'brush')
def update_filters(event=None):
filters = []
for k, v in (getattr(event, 'new') or {}).items():
filters.append(dict(field=k, type='>=', value=v[0]))
filters.append(dict(field=k, type='<=', value=v[1]))
table.filters = filters
update_table()
update_chart()
intro = """
This app provides an example of **building a simple dashboard using
Panel**.\n\nIt demonstrates how to take the output of **k-means
clustering on the Penguins dataset** using scikit-learn,
parameterizing the number of clusters and the variables to
plot.\n\nThe plot and the table are linked, i.e. selecting on the plot
will filter the data in the table.\n\n The **`x` marks the center** of
the cluster.
"""
await show(x, 'x-widget')
await show(y, 'y-widget')
await show(n_clusters, 'n-widget')
await show(intro, 'intro')
await show(chart, 'cluster-plot')
await show(table, 'table')
</py-script>
<script>
$(document).ready(function () {
$('#sidebarCollapse').on('click', function () {
$('#sidebar').toggleClass('active')
$(this).toggleClass('active')
var interval = setInterval(function () { window.dispatchEvent(new Event('resize')); }, 10);
setTimeout(function () { clearInterval(interval) }, 210)
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../build/bundle.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<div>
<py-repl id="my-repl" auto-generate="true"> </py-repl>
</div>
</body>
</html>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../build/bundle.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<div class="w-full h-full">
<div class="flex">
<div class="w-2/3"><py-repl id="my-repl" auto-generate="true" target="output"> </py-repl></div>
<div id="output" class="w-1/3"></div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<div id="outputDiv" style="background-color:yellow"></div>
<py-script target="outputDiv">
from datetime import datetime
now = datetime.now()
now.strftime("%m/%d/%Y, %H:%M:%S")
</py-script>
</body>
</html>

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../build/bundle.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<body>
<div id="outputDiv"></div>
<div id="outputDiv2" style="background-color: blueviolet"></div>
<div id="outputDiv3" style="background-color: yellow"></div>
<py-script target="outputDiv">
from datetime import datetime
now = datetime.now()
now.strftime("%m/%d/%Y, %H:%M:%S")
</py-script>
<py-script>
from datetime import datetime
import asyncio
async def foo():
while True:
await asyncio.sleep(1)
now = datetime.now()
output = now.strftime("%m/%d/%Y, %H:%M:%S")
pyscript.write("outputDiv2", output)
out3 = Element("outputDiv3")
if output[-1] in ["0", "4", "8"]:
out3.write("It's espresso time!")
else:
out3.clear()
pyscript.run_until_complete(foo())
</py-script>
</body>
</html>

View File

@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Todo App</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../build/bundle.css" />
<script defer src="../build/pyscript.js"></script>
</head>
<py-script>
from datetime import datetime as dt
tasks = []
# define the task template that will be use to render new templates to the page
task_template = Element("task-template").select('.task', from_content=True)
task_list = Element("list-tasks-container")
new_task_content = Element("new-task-content")
def add_task(*ags, **kws):
# create task
task_id = f"task-{len(tasks)}"
task = {"id": task_id, "content": new_task_content.element.value, "done": False, "created_at": dt.now()}
tasks.append(task)
# add the task element to the page as new node in the list by cloning from a template
taskHtml = task_template.clone(task_id, to=task_list)
taskHtmlContent = taskHtml.select('p')
taskHtmlContent.element.innerText = task['content']
taskHtmlCheck = taskHtml.select('input')
task_list.element.appendChild(taskHtml.element)
def check_task(evt=None):
task['done'] = not task['done']
if task['done']:
taskHtmlContent.element.classList.add("line-through")
else:
taskHtmlContent.element.classList.remove("line-through")
new_task_content.clear()
taskHtmlCheck.element.onclick = check_task
def add_task_event(e):
console.log("im in")
if (e.key == "Enter"):
add_task()
</py-script>
<main class="max-w-xs mx-auto mt-4">
<section>
<div class="text-center w-full mb-8">
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
</div>
<div>
<input id="new-task-content" class="border flex-1 mr-3 border-gray-300 p-2 rounded" type="text">
<button id="new-task-btn" class="p-2 text-white bg-blue-600 border border-blue-600 rounded" type="submit" pys-onClick="add_task">
Add task
</button>
</div>
<div id="list-tasks-container" class="flex flex-col-reverse mt-4">
</div>
<template id="task-template">
<section class="task bg-white my-1">
<label for="flex items-center p-2 ">
<input class="mr-2" type="checkbox" class="task-check">
<p class="m-0 inline"></p>
</label>
</section>
</template>
</section>
</main>
</html>